Подтвердить что ты не робот

Как передавать большие файлы с помощью JAXB Marshaller?

Проблема, с которой я столкнулась, состоит в том, как упорядочить большой список объектов в один XML файл, поэтому я не могу сортировать полный список за один шаг. У меня есть метод, который возвращает эти объекты в кусках, но затем я сортирую их с помощью JAXB, маршаллер возвращается с исключением, что эти объекты не являются корневыми элементами. Это нормально для нормального случая, когда вы хотите собрать весь документ за один шаг, но это также произойдет, если я установил для свойства JAXB_FRAGMENT значение true.

Это желаемый вывод XML:

<rootElem>  
    <startDescription></startDescription>  
    <repeatingElem></repeatingElem>
    <repeatingElem></repeatingElem>...
</rootElem>

Итак, я предполагаю, что мне нужен какой-то слушатель, который динамически загружает следующий фрагмент repeatingElements, чтобы передать его маршаллеру, прежде чем он напишет заключительный тег rootElement. Но как это сделать? До сих пор я использовал JAXB для сортировки небольших файлов, и в документации JAXB не было много намеков на этот случай использования.

4b9b3361

Ответ 1

Я знаю, что это старый вопрос, но я наткнулся на него, ища дубликаты другого подобного вопроса.

Как указывает @skaffman, вы хотите маршал с включенным JAXB_FRAGMENT и вашими объектами, завернутыми в JAXBElement. Затем вы неоднократно маршалируете каждый отдельный экземпляр повторяющегося элемента. По сути, похоже, что вы хотите что-то примерно такое:

public class StreamingMarshal<T>
{
    private XMLStreamWriter xmlOut;
    private Marshaller marshaller;
    private final Class<T> type;

    public StreamingMarshal(Class<T> type) throws JAXBException
    {
        this.type = type;
        JAXBContext context = JAXBContext.newInstance(type);
        Marshaller m = context.createMarshaller();
        m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
    }

    public void open(String filename) throws XMLStreamException, IOException
    {
        xmlOut = XMLOutputFactory.newFactory().createXMLStreamWriter(new FileOutputStream(filename));
        xmlOut.writeStartDocument();
        xmlOut.writeStartElement("rootElement");
    }

    public void write(T t) throws JAXBException
    {
        JAXBElement<T> element = new JAXBElement<T>(QName.valueOf(type.getSimpleName()), type, t);
        marshaller.marshal(element, xmlOut);
    }

    public void close() throws XMLStreamException
    {
        xmlOut.writeEndDocument();
        xmlOut.close();
    }
}

Ответ 2

Как вы обнаружили, если класс не имеет аннотации @XmlRootElement, то вы не можете передать экземпляр этого класса маршаллеру. Однако есть простой способ обернуть объект в JAXBElement и передать его маршаллеру.

Теперь JAXBElement - довольно неуклюжий зверь, но то, что он делает, содержит имя элемента и пространство имен объекта, который вы хотите маршалировать, информацию, которая обычно содержится в аннотации @XmlRootElement. До тех пор, пока у вас есть имя и пространство имен, вы можете построить JAXBElement для переноса POJO и маршалировать это.

Если ваши POJO были сгенерированы XJC, тогда он также сгенерировал класс ObjectFactory, который содержит методы factory для создания оберток JAXBElement для вас, что делает вещи немного проще.

Вам все равно придется использовать свойство JAXB_FRAGMENT для повторяющихся внутренних элементов, иначе JAXB будет генерировать такие вещи, как пролог XML каждый раз, чего вы не хотите.

Ответ 3

Я не знаю много JAXB, поэтому я не могу помочь. Но если вы не возражаете, у меня есть предложение.

Написание XML намного проще, чем чтение, поэтому решение вашей проблемы может заключаться в использовании более "низкоуровневого" подхода. Просто напишите свой собственный маршаллер, используя одну из доступных библиотек с открытым исходным кодом для XML. Я думаю, вы можете легко делать то, что хотите, dom4j.