使用 SAX 解析常见的 XML 元素

Posted

技术标签:

【中文标题】使用 SAX 解析常见的 XML 元素【英文标题】:Using SAX to parse common XML elements 【发布时间】:2011-03-25 06:11:28 【问题描述】:

我目前正在使用 SAX (Java) 来解析一些不同的 XML 文档,每个文档代表不同的数据并且具有略微不同的结构。因此,每个 XML 文档都由不同的 SAX 类(子类化DefaultHandler)处理。

但是,有一些 XML 结构可以出现在所有这些不同的文档中。理想情况下,我想告诉解析器“嘿,当您到达 complex_node 元素时,只需使用 ComplexNodeHandler 读取它,然后将结果返回给我。如果您到达 some_other_node,请使用 OtherNodeHandler阅读它并把结果还给我”。

但是,我看不到一个明显的方法来做到这一点。

我应该只是简单地创建一个可以读取我拥有的所有不同文档(并消除代码重复)的整体处理程序类,还是有更聪明的方法来处理这个?

【问题讨论】:

我希望/确定我刚刚错过了一些非常明显的解决方案! 是否需要 SAX?将 xpath 与 DOM、XOM 或 vtd-xml 一起使用怎么样? 因为 SAX 速度最快,使用的内存最少,这在移动设备上很重要(我最初问这个问题时忽略了)。 【参考方案1】:

以下是我对类似问题 (Skipping nodes with sax) 的回答。它演示了如何在 XMLReader 上交换内容处理程序。

在此示例中,ContentHandler 中的交换只是忽略所有事件,直到它放弃控制,但您可以轻松地调整这个概念。


您可以执行以下操作:

import javax.xml.parsers.SAXParser; 
import javax.xml.parsers.SAXParserFactory; 
import org.xml.sax.XMLReader; 

public class Demo  

    public static void main(String[] args) throws Exception  
        SAXParserFactory spf = SAXParserFactory.newInstance(); 
        SAXParser sp = spf.newSAXParser(); 
        XMLReader xr = sp.getXMLReader(); 
        xr.setContentHandler(new MyContentHandler(xr)); 
        xr.parse("input.xml"); 
     
 

MyContentHandler

这个类负责处理你的 XML 文档。当你点击一个你想忽略的节点时,你可以交换 IgnoringContentHandler 它将吞下该节点的所有事件。

import org.xml.sax.Attributes; 
import org.xml.sax.ContentHandler; 
import org.xml.sax.Locator; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 

public class MyContentHandler implements ContentHandler  

    private XMLReader xmlReader; 

    public MyContentHandler(XMLReader xmlReader)  
        this.xmlReader = xmlReader; 
     

    public void setDocumentLocator(Locator locator)  
     

    public void startDocument() throws SAXException  
     

    public void endDocument() throws SAXException  
     

    public void startPrefixMapping(String prefix, String uri) 
            throws SAXException  
     

    public void endPrefixMapping(String prefix) throws SAXException  
     

    public void startElement(String uri, String localName, String qName, 
            Attributes atts) throws SAXException  
        if("sodium".equals(qName))  
            xmlReader.setContentHandler(new IgnoringContentHandler(xmlReader, this)); 
         else  
            System.out.println("START " + qName); 
         
     

    public void endElement(String uri, String localName, String qName) 
            throws SAXException  
        System.out.println("END " + qName); 
     

    public void characters(char[] ch, int start, int length) 
            throws SAXException  
        System.out.println(new String(ch, start, length)); 
     

    public void ignorableWhitespace(char[] ch, int start, int length) 
            throws SAXException  
     

    public void processingInstruction(String target, String data) 
            throws SAXException  
     

    public void skippedEntity(String name) throws SAXException  
     

 

忽略内容处理程序

当 IgnoringContentHandler 完成吞咽事件时,它会将控制权交还给您的主 ContentHandler。

import org.xml.sax.Attributes; 
import org.xml.sax.ContentHandler; 
import org.xml.sax.Locator; 
import org.xml.sax.SAXException; 
import org.xml.sax.XMLReader; 

public class IgnoringContentHandler implements ContentHandler  

    private int depth = 1; 
    private XMLReader xmlReader; 
    private ContentHandler contentHandler; 

    public IgnoringContentHandler(XMLReader xmlReader, ContentHandler contentHandler)  
        this.contentHandler = contentHandler; 
        this.xmlReader = xmlReader; 
     

    public void setDocumentLocator(Locator locator)  
     

    public void startDocument() throws SAXException  
     

    public void endDocument() throws SAXException  
     

    public void startPrefixMapping(String prefix, String uri) 
            throws SAXException  
     

    public void endPrefixMapping(String prefix) throws SAXException  
     

    public void startElement(String uri, String localName, String qName, 
            Attributes atts) throws SAXException  
        depth++; 
     

    public void endElement(String uri, String localName, String qName) 
            throws SAXException  
        depth--; 
        if(0 == depth)  
           xmlReader.setContentHandler(contentHandler); 
         
     

    public void characters(char[] ch, int start, int length) 
            throws SAXException  
     

    public void ignorableWhitespace(char[] ch, int start, int length) 
            throws SAXException  
     

    public void processingInstruction(String target, String data) 
            throws SAXException  
     

    public void skippedEntity(String name) throws SAXException  
     

 

【讨论】:

嗯,没有意识到 XMLReader 可以以这种方式即时更改。绝对看起来是处理它的最佳方式。 XMLReader 就是为此而设计的,请参阅 download-llnw.oracle.com/javase/6/docs/api/org/xml/sax/… ,我们在 JAXB 实现 MOXy 中使用它,在进行 SAX 处理时,每个正在构建的对象都有一个 ContentHandler。 @Blaise Doughan 首先感谢您提供的解决方案,这正是我一直在寻找的。我有一个问题。在评估结构的深度以知道何时传回主内容处理程序之后有什么特别的想法吗?为此目的使用 endDocument() 方法有什么问题吗? @Octavian Damiean,解析器只会调用 endDocument 一次。这就是需要深度变量的原因。 @Blaise Doughan,嗯,也许出于我的目的,这样就可以了,因为我只有独立的 XML。所以我从头到尾都在解析,但只是针对不同的独立 XML 进行了分支。如果你想跳过基于某个标签的部分 XML,我猜你需要深度变量。感谢您的回答。【参考方案2】:

您可以有一个处理程序 (ComplexNodeHandler),它只处理文档的某些部分 (complex_node),并将所有其他部分传递给另一个处理程序。 ComplexNodeHandler 的构造函数会将另一个处理程序作为参数。我的意思是这样的:

class ComplexNodeHandler 

    private ContentHandler handlerForOtherNodes;

    public ComplexNodeHandler(ContentHandler handlerForOtherNodes) 
         this.handlerForOtherNodes = handlerForOtherNodes;
    

    ...

    public startElement(String uri, String localName, String qName, Attributes atts) 
        if (currently in complex node) 
            [handle complex node data] 
         else 
            // pass the event to the document specific handler
            handlerForOtherNodes.startElement(uri, localName, qName, atts);
       
     

    ...


可能还有更好的选择,因为我对 SAX 不太熟悉。为公共部分编写基本处理程序并继承它也可以工作,但我不确定在这里使用继承是否是个好主意。

【讨论】:

我考虑过这个,但很快确定它会变得相当复杂。我不仅要转发来自startElement 的呼叫,还要转发来自endElementcharacters 和错误处理程序的呼叫。

以上是关于使用 SAX 解析常见的 XML 元素的主要内容,如果未能解决你的问题,请参考以下文章

解析XML文件之使用SAM解析器

使用 SAX 解析器,如何解析具有相同名称标签但元素不同的 xml 文件?

如何使用 SAX 解析器在 XML 中添加元素?

使用 SAX 解析器解析 Xml

Java XML 解析:使用 SAX 获取内部 XML

如何使用 SAX 解析器解析 XML