我不知道该怎么做,但Pangea 向我展示了方法。首先,让我们创建一个 trait 来处理位置:
import org.xml.sax.{helpers, Locator, SAXParseException}
trait WithLocation extends helpers.DefaultHandler {
var locator: org.xml.sax.Locator = _
def printLocation(msg: String) {
println("%s at line %d, column %d" format (msg, locator.getLineNumber, locator.getColumnNumber))
}
// Get location
abstract override def setDocumentLocator(locator: Locator) {
this.locator = locator
super.setDocumentLocator(locator)
}
// Display location messages
abstract override def warning(e: SAXParseException) {
printLocation("warning")
super.warning(e)
}
abstract override def error(e: SAXParseException) {
printLocation("error")
super.error(e)
}
abstract override def fatalError(e: SAXParseException) {
printLocation("fatal error")
super.fatalError(e)
}
}
接下来,让我们创建自己的 loader 覆盖XMLLoader
以adapter
包含我们的 trait:
import scala.xml.{factory, parsing, Elem}
object MyLoader extends factory.XMLLoader[Elem] {
override def adapter = new parsing.NoBindingFactoryAdapter with WithLocation
}
这就是它的全部!该对象XML
几乎没有增加XMLLoader
- 基本上,save
方法。如果您觉得需要完全替换,您可能想查看它的源代码。但这只是当您想自己处理所有这些时,因为 Scala 已经具有产生错误的特性:
object MyLoader extends factory.XMLLoader[Elem] {
override def adapter = new parsing.NoBindingFactoryAdapter with parsing.ConsoleErrorHandler
}
顺便说一下,该ConsoleErrorHandler
特征从异常中提取其行和编号信息。出于我们的目的,我们也需要异常之外的位置(我假设)。
现在,要修改节点创建本身,请查看scala.xml.factory.FactoryAdapter
抽象方法。我已经确定了createNode
,但我在NoBindingFactoryAdapter
级别上覆盖,因为它返回Elem
而不是Node
,这使我能够添加属性。所以:
import org.xml.sax.Locator
import scala.xml._
import parsing.NoBindingFactoryAdapter
trait WithLocation extends NoBindingFactoryAdapter {
var locator: org.xml.sax.Locator = _
// Get location
abstract override def setDocumentLocator(locator: Locator) {
this.locator = locator
super.setDocumentLocator(locator)
}
abstract override def createNode(pre: String, label: String, attrs: MetaData, scope: NamespaceBinding, children: List[Node]): Elem = (
super.createNode(pre, label, attrs, scope, children)
% Attribute("line", Text(locator.getLineNumber.toString), Null)
% Attribute("column", Text(locator.getColumnNumber.toString), Null)
)
}
object MyLoader extends factory.XMLLoader[Elem] {
// Keeping ConsoleErrorHandler for good measure
override def adapter = new parsing.NoBindingFactoryAdapter with parsing.ConsoleErrorHandler with WithLocation
}
结果:
scala> MyLoader.loadString("<a><b/></a>")
res4: scala.xml.Elem = <a line="1" column="12"><b line="1" column="8"></b></a>
请注意,它获得了最后一个位置,即结束标记处的位置。这是可以通过覆盖startElement
来改进的一件事,以跟踪每个元素在堆栈中的开始位置,并endElement
从该堆栈弹出到var
used by createNode
。
好问题。我学到了很多!:-)