Как правильно разобрать XML с SAX? - PullRequest
5 голосов
/ 17 ноября 2010

Я получаю XML-документ от службы REST, который должен быть проанализирован с использованием SAX.Пожалуйста, смотрите следующий пример, сгенерированный из XSD.

Настройка парсера не является проблемой.Моя главная проблема - фактическая обработка в методах startElement(), endElement() и т. Д. Я не понимаю, как извлечь нужные элементы и сохранить их, поскольку они несколько «вложены».

Пример

ConnectionList может встречаться один или два раза и может содержать любое количество элементов Connection, которые, в свою очередь, содержат сведения о соединении.По сути, мне нужен список всех соединений с их Date, Transfers и Time.Нужно ли создавать по одному классу для каждого элемента?

Насколько я понял, мне нужно сделать следующее: Если парсер встречает ...

  • ConnectionList: Создать новый объект ConnectionList и поместить его в список ConnectionList s
  • Connection: Создать новый объект Connection и поместить его в список подключений
  • Date, Transfers, Time (только если родительский элемент Duration): сохранить значение узла в текущем Connection объекте

.Фрагмент, как я могу этого достичь.

Спасибо: -)

Роберт

<?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 голосов
/ 17 ноября 2010

Наилучший способ парсинга XML с использованием SAX (на данный момент) - использование стека и условных операторов в соответствующих обратных вызовах. Вот статья, описывающая это , и мое краткое изложение этого:

Основная предпосылка заключается в том, что когда вы анализируете документ, вы создаете объекты для хранения проанализированных данных, помещая их в стек по мере необходимости, просматривая верхнюю часть стека, чтобы добавить данные к текущему элементу, и на конец каждого элемента выталкивает его из стека и сохраняет его в родительском элементе.

В результате вы сначала анализируете дерево глубины элементов, а в конце каждой ветви вы откатываете его обратно к родительскому элементу, пока не останетесь с единственным объектом (таким как ConnectionList), который содержит все проанализированные данные готовы к использованию. По сути, вы получаете серию объектов, которые отражают структуру исходного XML

Это означает, что вам нужны некоторые объекты данных, которые могут хранить данные в той же структуре, что и XML. Сложные элементы обычно становятся классами, в то время как простые элементы обычно являются атрибутами внутри классов. Корневой элемент часто представлен каким-то списком.

Для начала вы создаете объект стека для хранения данных при их разборе.

Затем в начале каждого элемента вы определяете тип используемого им метода localName.equals(), создаете экземпляр соответствующего класса и помещаете его в стек. Если элемент является простым элементом, вы, вероятно, будете смоделировать его как атрибут в классе, представляющем родительский элемент, и вам понадобится серия флагов, которые сообщают парсеру, встречается ли такой элемент и что это за элемент. обрабатываться в методе characters().

Фактические данные читаются с использованием метода characters(), и вы снова используете условную логику, чтобы определить, что делать с данными, основываясь на значении флага. По сути, вы заглядываете на вершину стека и используете соответствующий метод для записи данных в объект, преобразовывая из текста, где это необходимо.

В конце каждого элемента вы открываете верх стека и снова используете localName.equals(), чтобы определить, как сохранить его в объекте перед ним (например, какой метод установки необходимо вызвать)

Когда вы дойдете до конца документа, вы должны были собрать все данные в документе.

6 голосов
/ 17 ноября 2010

Ваш обработчик событий SAX должен действовать как конечный автомат.Ваша структура довольно глубока, поэтому конечный автомат будет немного сложным;но это основной подход:

Все переменные являются переменными-членами.

Когда вы сталкиваетесь с событием startElement, вы создаете экземпляр объекта, представляющего этот элемент, затем помещаете объект в стек (или устанавливаетефлаг, указывающий, с каким значением вы работаете).

Когда вы встречаете текстовое событие, прочитайте текст и установите соответствующее значение на основе флага, установленного на предыдущем шаге.

Когда вы сталкиваетесь с событием endElement, вы тянете текущий объектвыключите стек и вызовите установщик объекта, который сейчас находится на вершине стека.

Когда вы исчерпали документ, у вас должен остаться только один объект в стеке, который представляет все, что вы прочитали.

1 голос
/ 17 ноября 2010

SAX-парсеры немного похожи на просмотр большого изображения через крошечное шпионское отверстие.

Обратный вызов покажет вам один фрагмент структуры XML за раз. Он не даст вам никаких подсказок относительно того, где вы находитесь в документе, представлен только один фрагмент данных. Имя элемента, имя / значение атрибута или текстовое содержимое.

Ваша программа должна отслеживать, где вы находитесь в документе. Если вы выполняете синтаксический анализ на лету, подойдет простая структура стека - вы помещаете имя в стек, когда получаете «beginelement», и вставляете стек в «endelement».

Если вы обнаружите, что строите древовидную структуру, я бы переключился на парсер DOM, поскольку все, что вы напишите, будет бледной и глючной тенью чего-то вроде XERCES.

1 голос
/ 17 ноября 2010

Обычно я помещаю объекты в стек и вставляю / извлекаю их при разборе файла XML (особенно полезно, если объекты вложены, но это не ваш случай).

Если вы хотите более простой подход, вам нужен указатель на текущий ConnectionList и на текущее Connection. Поскольку вы уже знаете структуру вашего файла, это может быть проще, чем использование стекового парсера.

1 голос
/ 17 ноября 2010

Вообще говоря, у вас есть пара вариантов:

  1. Использование пользовательских объектов для сопоставления XML, эти объекты будут инкапсулировать больше объектов, подобно гнезду элементов XML.
  2. Выполнитьобщий анализ и обход DOM через относительные элементы.

Насколько мне известно, существуют некоторые инструменты, такие как JAXB, которые будут генерировать ваши классы на основе XSD, но иногда они могут иметь цену, как это часто бывает сгенерированным кодом.

Если вы выберете опцию 1 и «накатите свой», вам нужно будет предоставить методы для демаршалинга и маршалинга, которые идут в и из XML и, наиболее вероятно, в Strings.Что-то вроде:

<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 );
}

Надеюсь, это поможет.

1 голос
/ 17 ноября 2010

Если это достаточно маленький XML-документ и ограничения по памяти и пропускной способности не являются препятствием для решения в памяти, тогда вы можете использовать вместо этого JAXB.Вы можете сгенерировать необходимые классы из XSD и просто распаковать xml в объекты java.Если вам нужно использовать потоковый анализатор, тогда рассмотрите возможность использования StAX, я нахожу это более интуитивно понятным.

...