Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

JAXB и XSLT с применением StAX

Anna | 5.06.2014 | нет комментариев

В одном из планов потребовалось обрабатывать крупные XML файлы, от сотен мегабайт до десятков гигабайт.
Причем выдернуть нужно было только некоторые тэги с расположенные на разной «глубине». XSLT «в лоб» ломался от недостатка памяти. Пришлось подумать и припомнить о потоковом парсере.

Существуют несколько моделей обработки XML. Особенно вестимые — DOM и SAX.
DOM грузит каждый XML документ, строит его внутреннее представление и предоставляет вероятность навигации по каждому документу. SAX наоборот, читает входной документ и при распознавании элемента вызывает handler’ы для обработки.

В моем случае DOM отпал по причине потребляемой памяти. SAX API построено на handler’ах, в итоге код получается менее читабельным. StAX представляет из себя потоковый парсер (как и SAX), но API построено на тезисе pull. То есть распознанные элементы «вынимаются» из потока по требованию.

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

Данные плана закрыты NDA, следственно в статье не применяются.

И так, есть дальнейший

XML документ

<data>
    <dtype_one>
        <p1>p1_data_1</p1>
        <p2>p1_data_1</p2>
        <p3>p1_data_1</p3>
        <p4>p1_data_1</p4>
        <p5>p1_data_1</p5>
    </dtype_one>
    <dtype_two>
        <p1>p1_data_2</p1>
        <p2>p1_data_2</p2>
        <p3>p1_data_2</p3>
        <p4>p1_data_2</p4>
        <p5>p1_data_2</p5>
    </dtype_two>
    <WS>
        <dtype_three>
            <p1>p1_data_3</p1>
            <p2>p1_data_3</p2>
            <p3>p1_data_3</p3>
            <p4>p1_data_3</p4>
            <p5>p1_data_3</p5>
        </dtype_three>
    </WS>
</data>

из него необходимо выделить и обработать тэги dtype_one, dtype_two и dtype_three. Тэги повторяются в документе. Берем

схему документа

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
           xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="data" type="dataType"/>
    <xs:element name="dtype_one" type="dtype_oneType"/>
    <xs:element name="dtype_two" type="dtype_twoType"/>
    <xs:element name="dtype_three" type="dtype_threeType"/>

    <xs:complexType name="dtype_oneType">
        <xs:sequence>
            <xs:element type="xs:string" name="p1"/>
            <xs:element type="xs:string" name="p2"/>
            <xs:element type="xs:string" name="p3"/>
            <xs:element type="xs:string" name="p4"/>
            <xs:element type="xs:string" name="p5"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="dataType">
        <xs:sequence>
            <xs:element type="dtype_oneType" name="dtype_one"/>
            <xs:element type="dtype_twoType" name="dtype_two"/>
            <xs:element type="WSType" name="WS"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="WSType">
        <xs:sequence>
            <xs:element type="dtype_threeType" name="dtype_three"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="dtype_twoType">
        <xs:sequence>
            <xs:element type="xs:string" name="p1"/>
            <xs:element type="xs:string" name="p2"/>
            <xs:element type="xs:string" name="p3"/>
            <xs:element type="xs:string" name="p4"/>
            <xs:element type="xs:string" name="p5"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="dtype_threeType">
        <xs:sequence>
            <xs:element type="xs:string" name="p1"/>
            <xs:element type="xs:string" name="p2"/>
            <xs:element type="xs:string" name="p3"/>
            <xs:element type="xs:string" name="p4"/>
            <xs:element type="xs:string" name="p5"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

и убеждаемся что в нем есть элементы «element» надобных нам тэгов:

    <xs:element name="dtype_one" type="dtype_oneType"/>
    <xs:element name="dtype_two" type="dtype_twoType"/>
    <xs:element name="dtype_three" type="dtype_threeType"/>

 

если схемы нет, IDEA отменно может сгенерить ее по xml файлу.

Это необходимо для того, что бы XJC сгенерил аннотацию @XmlRootElement. План собирается maven, для вызова XJC применяется maven-jaxb2-plugin. Для генерации @XmlRootElement для всех «element» в файле схемы, нужно добавить следующие строки в файл bindings.xjb:

<jaxb:bindings>
        <jaxb:globalBindings >
            <xjc:simple/>
        </jaxb:globalBindings>
    </jaxb:bindings>

и подключить его в конфигурации плагина maven-jaxb2-plugin, в pom.xml

<bindingDirectory>${project.basedir}/xjb</bindingDirectory>

Сейчас собственно к коду, класс TagEngine хранит список обработчиков тэгов и занимается разбором:

    public void process(InputStream inputStream) throws FileNotFoundException,
            XMLStreamException, TransformerException {
// Создаем XMLStreamReader, поток разбора
        XMLInputFactory factory = XMLInputFactory.newFactory();
        XMLStreamReader streamReader = factory.createXMLStreamReader(inputStream);
// Стэк тэгов
        Stack<String> tagStack = new Stack<String>();
// Пока есть тэги
        while (streamReader.hasNext()) {
// Берем дальнейший
            int eventType = streamReader.next();
// Если старт элемента
            if(eventType == XMLStreamConstants.START_ELEMENT) {
// Помещаем в стэк
                tagStack.push(streamReader.getName().toString());
// Ищем совпадение с обработчиком
                TagProcessor t = processorMap.get(tagStack);

                if(t != null) {
// Обнаружили, обрабатываем
                    t.process(streamReader);
                    tagStack.pop();
                }
            } else if(eventType == XMLStreamConstants.END_ELEMENT) {
                tagStack.pop();
            }
        }
    }

Класс JAXBProcessor занимается unmarshalling’ом выделенных элементов. Класс XSLTProcessor вызывает XSLT реформирования. Вот так выглядит класс исполняющий пригодную работу:

public class DataOne extends JAXBProcessor<DtypeOne> {
    private static final String TAG_NAME = "data/dtype_one";

// Конструктор 
    public DataOne() throws JAXBException, SAXException {
        super(DtypeOne.class, TAG_NAME);
    }
// Конструктор с подключением схемы для валидации
    public DataOne(String schemaFileName) throws JAXBException, SAXException {
        super(DtypeOne.class, TAG_NAME, schemaFileName);
    }
// Тут обрабатываем полученный из XML объект
    @Override
    public void doWork(DtypeOne element) {
//        System.out.println(element.getP1());
    }
}

Пример использования XSLT DataThreeXSLT.

Пример запуска (эмулируется обработка 277 мегабайтного файла):

JAXB unmarshall without schema validation
Runtime: 8034ms, 277000015 bytes processed
Used Memory:80MB
JAXB unmarshall with schema validation
Runtime: 66180ms, 277000015 bytes processed
Used Memory:56MB
XSLT processing
Runtime: 10604ms, 277000015 bytes processed
Used Memory:231MB

С памятью все отлично, валидация безусловно крепко тормозит обработку.

PS. Для тестов применял Mockito (прежде применял jmock). Понравилась вероятность spy — перехват вызовов и их параметров при работе с живым (не mock) объектом.
PPS. Код плана на GitHub.

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB