Может ли JAXB анализировать большие XML-файлы по частям - PullRequest
22 голосов
/ 16 июля 2009

Мне нужно проанализировать потенциально большие XML-файлы, схема которых уже предоставлена ​​мне в нескольких XSD-файлах, поэтому привязка к XML очень рекомендуется. Я хотел бы знать, могу ли я использовать JAXB для разбора файла на куски, и если да, то как.

Ответы [ 3 ]

26 голосов
/ 13 февраля 2012

Поскольку код имеет значение, вот PartialUnmarshaller, который считывает большой файл на куски. Можно использовать таким образом new PartialUnmarshaller<YourClass>(stream, YourClass.class)

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.*;
import java.io.InputStream;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static javax.xml.stream.XMLStreamConstants.*;

public class PartialUnmarshaller<T> {
    XMLStreamReader reader;
    Class<T> clazz;
    Unmarshaller unmarshaller;

    public PartialUnmarshaller(InputStream stream, Class<T> clazz) throws XMLStreamException, FactoryConfigurationError, JAXBException {
        this.clazz = clazz;
        this.unmarshaller = JAXBContext.newInstance(clazz).createUnmarshaller();
        this.reader = XMLInputFactory.newInstance().createXMLStreamReader(stream);

        /* ignore headers */
        skipElements(START_DOCUMENT, DTD);
        /* ignore root element */
        reader.nextTag();
        /* if there's no tag, ignore root element's end */
        skipElements(END_ELEMENT);
    }

    public T next() throws XMLStreamException, JAXBException {
        if (!hasNext())
            throw new NoSuchElementException();

        T value = unmarshaller.unmarshal(reader, clazz).getValue();

        skipElements(CHARACTERS, END_ELEMENT);
        return value;
    }

    public boolean hasNext() throws XMLStreamException {
        return reader.hasNext();
    }

    public void close() throws XMLStreamException {
        reader.close();
    }

    void skipElements(int... elements) throws XMLStreamException {
        int eventType = reader.getEventType();

        List<Integer> types = asList(elements);
        while (types.contains(eventType))
            eventType = reader.next();
    }
}
18 голосов
/ 16 июля 2009

Это подробно описано в руководстве пользователя . Загрузка JAXB с http://jaxb.java.net/ включает пример того, как анализировать один блок за раз.

Когда документ большой, это как правило, потому что есть повторяющиеся части в нем. Возможно это покупка заказ с большим списком позиций, или, возможно, это файл журнала XML с большое количество записей в журнале.

Этот тип XML подходит для Кусок обработки; основная идея заключается в использовать API StAX, запустить цикл и unmarshal отдельные куски по отдельности. Ваша программа действует на один кусок, а затем выбрасывает его. Таким образом, вы будете только держать в самый большой кусок памяти, который позволяет вам обрабатывать большие документы.

См. Потоковое-unmarshalling пример и частичное демарширование пример в распределении JAXB RI больше о том, как это сделать. Потоково-unmarshalling пример имеет Преимущество в том, что он может обрабатывать куски в произвольный уровень гнезда, но требует вам иметь дело с моделью пуша --- JAXB unmarshaller будет «толкать» новый кусок вам и вам нужно обработайте их прямо там.

Напротив, частичное разборчивое Пример работает в модели тянуть (который обычно облегчает обработку), но этот подход имеет некоторые ограничения в частях привязки данных, кроме повторная часть.

2 голосов
/ 10 октября 2015

Yves Amsellem отвечает довольно хорошо, но работает, только если все элементы имеют одинаковый тип. В противном случае ваш unmarshall выдаст исключение, но читатель уже использует байты, поэтому вы не сможете восстановить. Вместо этого мы должны следовать совету Скаффмана и посмотреть на образец из банки JAXB.

Чтобы объяснить, как это работает:

  1. Создание демаршаллера JAXB.
  2. Добавьте слушателя к маршаллеру для перехвата соответствующих элементов. Это делается путем «взлома» ArrayList, чтобы гарантировать, что элементы не будут сохранены в памяти после разархивирования.
  3. Создать SAX-парсер. Здесь происходит потоковая передача.
  4. Используйте unmarshaller для генерации обработчика для парсера SAX.
  5. поток!

Я изменил решение, чтобы оно было общим *. Однако это потребовало некоторых размышлений. Если это не так, пожалуйста, посмотрите примеры кода в банках JAXB.

ArrayListAddInterceptor.java

import java.lang.reflect.Field;
import java.util.ArrayList;

public class ArrayListAddInterceptor<T> extends ArrayList<T> {
    private static final long serialVersionUID = 1L;

    private AddInterceptor<T> interceptor;

    public ArrayListAddInterceptor(AddInterceptor<T> interceptor) {
        this.interceptor = interceptor;
    }

    @Override
    public boolean add(T t) {
        interceptor.intercept(t);
        return false;
    }

    public static interface AddInterceptor<T> {
        public void intercept(T t);
    }

    public static void apply(AddInterceptor<?> interceptor, Object o, String property) {
        try {
            Field field = o.getClass().getDeclaredField(property);
            field.setAccessible(true);
            field.set(o, new ArrayListAddInterceptor(interceptor));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

Main.java

public class Main {
  public void parsePurchaseOrders(AddInterceptor<PurchaseOrder> interceptor, List<File> files) {
        try {
            // create JAXBContext for the primer.xsd
            JAXBContext context = JAXBContext.newInstance("primer");

            Unmarshaller unmarshaller = context.createUnmarshaller();

            // install the callback on all PurchaseOrders instances
            unmarshaller.setListener(new Unmarshaller.Listener() {
                public void beforeUnmarshal(Object target, Object parent) {
                    if (target instanceof PurchaseOrders) {
                        ArrayListAddInterceptor.apply(interceptor, target, "purchaseOrder");
                    }
                }
            });

            // create a new XML parser
            SAXParserFactory factory = SAXParserFactory.newInstance();
            factory.setNamespaceAware(true);
            XMLReader reader = factory.newSAXParser().getXMLReader();
            reader.setContentHandler(unmarshaller.getUnmarshallerHandler());

            for (File file : files) {
                reader.parse(new InputSource(new FileInputStream(file)));
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

* Этот код не был проверен и предназначен только для иллюстративных целей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...