如何跟踪XML元素的源代码行(位置)?

前端之家收集整理的这篇文章主要介绍了如何跟踪XML元素的源代码行(位置)?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我假设这个问题可能没有令人满意的答案,但是如果我错过了一些事情,我会问这个问题.

基本上,我想在源文档中找到源于某个XML元素的行,给定元素实例.我希望这只是为了更好的诊断错误消息 – XML是配置文件的一部分,如果出现问题,我希望能够将读取器的错误消息指向XML文档中正确的位置所以他可以纠正错误.

我了解标准的Scala XML支持可能没有这样的内置功能.毕竟,使用这些信息来注释每一个NodeSeq实例都是浪费的,而不是每个XML元素甚至都有一个源文档被解析.在我看来,标准的Scala XML解析器将线信息丢弃,之后就无法检索它.

但是切换到另一个XML框架不是一个选择.为了更好的诊断错误消息,添加另一个库依赖关系似乎不适合我.另外,尽管有一些缺点,我真的很喜欢内置的XML模式匹配支持.

我唯一的希望是,您可以向我显示一种方法来更改或子类化标准的Scala XML解析器,以使其生成的节点将以源行编号进行注释.也许可以创建一个NodeSeq的一个特殊的子类.或者也许只有Atom可以被子类化,因为NodeSeq太动态了?我不知道.

无论如何,我的希望接近于零.我不认为解析器中有一个地方可以挂钩改变节点的创建方式,而在那个位置,线路信息是可用的.不过,我想知道为什么我以前没有发现这个问题.如果这是重复的,请指出原件.

我不知道该怎么做,但 Pangea showed me the way.首先,我们创建一个特征来处理位置:
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)
    }
}

接下来,让我们创建我们自己的加载器覆盖XMLLoader的适配器以包含我们的特征:

import scala.xml.{factory,parsing,Elem}
object MyLoader extends factory.XMLLoader[Elem] {
    override def adapter = new parsing.NoBindingFactoryAdapter with WithLocation
}

这就是它的一切!对象XML对XMLLoader几乎没有增加 – 基本上是保存方法.如果您需要完全替换,您可能需要查看其源代码.但是,只有当您想自己处理所有这些时,才可以,因为Scala已经具有产生错误的特征:

object MyLoader extends factory.XMLLoader[Elem] {
    override def adapter = new parsing.NoBindingFactoryAdapter with parsing.ConsoleErrorHandler
}

ConsoleErrorHandler trait从异常中提取其行和数字信息,顺便说一句.为了我们的目的,我们需要除了异常之外的位置(我假设).

现在,要修改节点创建本身,请查看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从这个堆栈中弹出到由createNode使用的var中可以改进.

好问题我学到了很多!

猜你在找的XML相关文章