Причем выдернуть надо было только некоторые тэги с расположенные на различной «глубине». XSLT «в лоб» ломался от недостатка памяти. Пришлось подумать и вспомнить о потоковом парсере.
Существуют несколько моделей обработки XML. Наиболее известные — DOM и SAX.
DOM грузит весь XML документ, строит его внутреннее представление и предоставляет возможность навигации по всему документу. SAX напротив, читает входной документ и при распознавании элемента вызывает handler’ы для обработки.
В моем случае DOM отпал по причине потребляемой памяти. SAX API построено на handler’ах, в результате код получается менее читабельным. StAX представляет из себя потоковый парсер (как и SAX), но API построено на принципе pull. То есть распознанные элементы «вынимаются» из потока по требованию.
Поскольку структуры данных подпадающие под обработку были весьма сложными и разнообразными, а обработка достаточно нетривиальная, решено было использовать JAXB для перевода во внутреннее представление.
Данные проекта закрыты NDA, поэтому в статье не используются.
И так, есть следующий
<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) объектом.
ссылка на оригинал статьи http://habrahabr.ru/post/185360/
Добавить комментарий