如何使用SAX正确解析XML?

5
我从REST服务接收到一个XML文档,需要使用SAX解析。请看下面这个例子,它是由XSD生成的。
设置解析器不是问题。我的主要问题在于实际处理startElement(), endElement()等方法。我不知道如何提取所需的项目并将它们存储,因为它们有些“嵌套”。
示例 ConnectionList可能出现一次或两次,并且可以包含任意数量的Connection元素,这些元素反过来又有关于连接的详细信息。基本上,我需要所有连接的DateTransfersTime列表。我是否需要为每个元素创建一个类?
根据我的理解,我需要做以下几件事: 如果解析器遇到...
- ConnectionList:创建新的ConnectionList对象并将其放入ConnectionList列表中。 - Connection:创建一个新的Connection对象并将其放入Connections列表中。 - DateTransfersTime (仅当父级是Duration时):将节点值存储在当前Connection对象中。
我非常感谢任何帮助,提示,想法和代码片段,以便我可以实现这个功能。
谢谢 :-)
Robert
<?xml version="1.0" encoding="UTF-8"?>
<ResC xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <Err code="r5E5a1Wm" text="tk-gWYbw" level="E"/>
    <Err code="takVDd34" text="XtvyjmjPuscK" level="E"/>
    <Err code="hQ1-:aDQ" text="YWc5qtY.gkwCeJW2S" level="E"/>
    <ConRes dir="R">
        <Err code="ZfwPC:tj" text="RKKFuLXoM0oOfp3a" level="E"/>
        <Err code="bhDjSJPa" text="BJoHuOMdwzhcddW" level="E"/>
        <Err code="CX-NhK9r" text="j55qy-WiNPXu" level="E"/>
        <ConResCtxt b="1" f="1">0815</ConResCtxt>
        <ConnectionList type="IV">
            <Err code="WI3WX.jo" text="rK3H5jwa-Zfen3" level="E"/>
            <Connection id="ID000">
                <Overview>
                    <Date>b3lcM_Yiyq7dqL9</Date>
                    <Departure>
                        <BasicStop type="NORMAL" index="-1086549314">
                            <Address externalId="t.EdKe93xkqFqLwPzgd-4vHSJemy8"
                                externalStationNr="1332105793" name="fdREYJPu83WV503V8szdCX"
                                x="951177990" y="-1579782776" z="1807457957" type="WGS84"/>
                        </BasicStop>
                    </Departure>
                    <Arrival>
                        <BasicStop type="NORMAL" index="1897526979">
                            <Address externalId="l7h_GTUit6fv" externalStationNr="-1670310329"
                                name="WJznDTzkTvyET51pfr7X" x="-1738098662" y="-170353174"
                                z="-475585957" type="WGS84"/>
                        </BasicStop>
                    </Arrival>
                    <Transfers>dZbgZfDH8j1hb1i</Transfers>
                    <Duration>
                        <Time>00d00:18:00</Time>
                    </Duration>
                    <ServiceDays> </ServiceDays>
                    <Products>
                        <Product cat="qmrN2dShHJp"/>
                        <Product cat="Hg"/>
                        <Product cat="nurxhdl3w.P0x7FRv2J3UoF"/>
                    </Products>
                    <ContextURL url="http://FzgEqiVC/"/>
                </Overview>
            </Connection>
            <Connection id="ID004">
                <Overview>
                    <Date>W5a47DRkc7XDZjhwq_s5Un.</Date>
                    <Departure>
                        <BasicStop type="NORMAL" index="-1014429844">
                            <Address externalId="RMnzjEFOTTdM1oaAUw" externalStationNr="1429101638"
                                name="HF-1" x="1005198487" y="570832676" z="975615566" type="WGS84"
                            />
                        </BasicStop>
                    </Departure>
                    <Arrival>
                        <BasicStop type="NORMAL" index="-58308182">
                            <Address externalId="rVdwdQvAukfj2QcA7b3OSdGOyW"
                                externalStationNr="1142334006" name="g" x="-1791416159"
                                y="-541300941" z="478129823" type="WGS84"/>
                        </BasicStop>
                    </Arrival>
                    <Transfers>GG56XN6zgiJF804mE_N4o</Transfers>
                    <Duration> </Duration>
                    <ServiceDays> </ServiceDays>
                    <Products>
                        <Product cat="fs_Oyoy9NYBai-qaxbty6j9Y7r1St"/>
                        <Product cat="P2CbaSGpC"/>
                        <Product cat="CGZrqSIDM6M4kUlb8_xZ8jRlH4c"/>
                    </Products>
                    <ContextURL url="http://JkRhuXtu/"/>
                </Overview>
            </Connection>
        </ConnectionList>
        <ConnectionList type="IV">
            <Err code="0lFWRY2X" text="KLmdczFRhV" level="E"/>
            <Connection id="ID012">
                <Overview>
                    <Date>t8mn634zjCZsRPyxj_e_-UYMH</Date>
                    <Departure>
                        <BasicStop type="NORMAL" index="-2095085423">
                            <Address externalId="ftKAFG-Uk7x" externalStationNr="1390920810"
                                name="JQrQXOQbm.FLaCMeSiTYjT" x="1970142849" y="-655980297"
                                z="2102464970" type="WGS84"/>
                        </BasicStop>
                    </Departure>
                    <Arrival>
                        <BasicStop type="NORMAL" index="1552118247">
                            <Address externalId="qcBpeuPDRzvSt1o" externalStationNr="-1133118359"
                                name="AJiJOB1t" x="-1422533132" y="-1158953133" z="484831466"
                                type="WGS84"/>
                        </BasicStop>
                    </Arrival>
                    <Transfers>D0MiUwW9nuuM_uykvawg2C07pwHL</Transfers>
                    <Duration> </Duration>
                    <ServiceDays> </ServiceDays>
                    <Products>
                        <Product cat="LpGOZbLDbJm"/>
                        <Product cat="JIv-szQVX2icPb"/>
                        <Product cat="Q7-pthWoOT"/>
                    </Products>
                    <ContextURL url="http://zGWgivvi/"/>
                </Overview>
                <IList>
                    <I header="ze4Wt3hVD-DvjujY6QKae" text="lVwB4RxAHcYq3.F"
                        uriCustom="iVjQJCoU1MVOv2Z9lwarP"/>
                    <I header="z-i.au59soMzXLZCbV" text="PoTP" uriCustom="ksrbwEH6scNR"/>
                    <I header="N" text="jHDA4" uriCustom="ub95811lMIa_495ZbPOuNWL0rRWh"/>
                </IList>
                <CommentList>
                    <Comment id="ID013">
                        <Text lang="EN"> </Text>
                        <Text lang="FR"> </Text>
                        <Text lang="PL"> </Text>
                    </Comment>
                    <Comment id="ID014">
                        <Text lang="DK"> </Text>
                        <Text lang="IT"> </Text>
                        <Text lang="IT"> </Text>
                    </Comment>
                    <Comment id="ID015">
                        <Text lang="MACRO"> </Text>
                        <Text lang="IT"> </Text>
                        <Text lang="EN"> </Text>
                    </Comment>
                </CommentList>
            </Connection>
        </ConnectionList>
    </ConRes>
</ResC>
6个回答

7
我目前发现使用SAX解析XML的最佳方法是在相关回调中使用堆栈和条件语句。这是一篇描述它的文章,以下是我的总结:
基本原则是,在解析文档时,创建对象来存储解析数据,将它们推入堆栈并在当前元素中查看堆栈顶部以添加数据,并在每个元素结束时将其弹出堆栈并存储在父元素中。
效果是您按深度优先方式解析元素树,并在每个分支的末尾将其回滚到父元素中,直到剩下一个包含所有解析数据的单个对象(例如ConnectionList)可供使用。实质上,您最终会得到一系列对象,这些对象镜像了原始XML的结构。
这意味着您需要一些数据对象,可以将数据以与XML相同的结构存储。复杂元素通常会成为类,而简单元素通常会成为类中的属性。根元素通常由某种列表表示。
首先,您需要创建一个堆栈对象来保存解析数据。
然后,在每个元素的开头,您可以使用localName.equals()方法来确定其类型,创建适当类的实例,并将其推入堆栈。如果元素是简单元素,则可能将其建模为表示父元素的类中的属性,并且您需要一系列标志来告诉解析器是否遇到此类元素以及它是什么元素,以便可以在characters()方法中处理它。
实际数据使用characters()方法读取,再次使用条件逻辑根据标志值确定如何处理数据。基本上,您查看堆栈顶部并使用适当的方法将数据写入对象中,必要时从文本转换。
在每个元素的末尾,弹出堆栈顶部,并再次使用localName.equals()确定如何将其存储在前一个对象中(例如,需要调用哪个setter方法)。
当您到达文档结尾时,应该已经捕获了文档中的所有数据。

谢谢,克里斯!我想这就是我一直在寻找的 :-) - Robert Strauch
我通常对stax/sax解析的第一个建议是尽可能避免使用它们... - vtd-xml-author

6

你的SAX事件处理程序应该充当一个状态机。由于你的结构相当深,所以状态机会有点复杂;但这是基本的方法:

所有变量都是成员变量。

当你遇到一个startElement事件时,你实例化一个表示该元素的对象,然后将对象放在堆栈上(或设置一个指示你正在处理哪个值的标志)。

当你遇到一个text事件时,读取文本并根据你在上一步中设置的标志设置适当的值。

当你遇到一个endElement事件时,你从堆栈中取出当前对象,并调用现在在堆栈顶部的对象上的setter。

当你耗尽文档时,你应该只剩下一个对象留在堆栈上,它代表了你读取的所有内容。


2
SAX解析器有点像通过小间谍孔看大图片。回调函数每次只会呈现给您一个XML结构的单个部分,不会给您任何关于文档位置的线索,只会呈现单个数据,如元素名称、属性名称/值或文本内容。您的程序需要跟踪您在文档中的位置。如果您正在进行实时解析,则简单的堆栈结构就足够了——当您获得“beginelement”时将名称推入堆栈,当您获得“endelement”时弹出堆栈。如果您发现自己正在构建树形结构,我建议切换到DOM解析器,因为您编写的任何内容都将是XERCES之类东西的苍白而有缺陷的影子。

1
如果 XML 文档较小且内存/吞吐量的限制不禁止使用内存解决方案,那么您可以使用 JAXB。您可以从 XSD 生成所需的类,并将 xml 反序列化为 Java 对象。如果必须使用流式解析器,则考虑改用 StAX,我通常觉得这更直观。

谢谢,埃利斯。我忘了提到这将在一个只支持SAX和DOM的Android设备上运行。 - Robert Strauch

1
通常我会将对象放在堆栈上,在解析XML文件时进行推入/弹出操作(如果对象嵌套,这非常有用,但这不是你的情况)。
如果你想要一个更简单的方法,你需要一个指向当前ConnectionList和当前Connection的指针。由于你已经知道了文件的结构,所以这可能比使用基于堆栈的解析器更容易。

1

一般来说,您有几个选择:

  1. 使用自定义对象将XML映射到这些对象中,这些对象将封装更多的对象,就像XML元素嵌套一样。
  2. 进行通用解析并通过相关元素遍历DOM。

据我所知,有一些工具可以生成基于XSD的类,例如JAXB,但它们有时会带来代价,因为生成的代码通常是如此。

如果您选择选项1并“自己动手”,则需要提供用于从XML和最可能的字符串转换的反编组和编组方法。类似于:

<Foo>
  <Bar>
    <Baz></Baz>
  </Bar>
  <Thing></Thing>
</Foo>

// pseudo-code!
//In Foo.java
unmarshal( Element element ) {
 unmarshalBar( element );
 unmarshalThing( element );
}

unmarshalBar( Element element ) {
 //...verify that the element is bar
 bar = new Bar();
 bar.unmarshal( element );
}

//In Bar.java
unmarshal( Element element ) {
 unmarshalBaz( element );
}

希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接