Как передавать большие файлы с помощью JAXB Marshaller?
Проблема, с которой я столкнулась, состоит в том, как упорядочить большой список объектов в один XML файл, поэтому я не могу сортировать полный список за один шаг. У меня есть метод, который возвращает эти объекты в кусках, но затем я сортирую их с помощью JAXB, маршаллер возвращается с исключением, что эти объекты не являются корневыми элементами. Это нормально для нормального случая, когда вы хотите собрать весь документ за один шаг, но это также произойдет, если я установил для свойства JAXB_FRAGMENT значение true.
Это желаемый вывод XML:
<rootElem>
<startDescription></startDescription>
<repeatingElem></repeatingElem>
<repeatingElem></repeatingElem>...
</rootElem>
Итак, я предполагаю, что мне нужен какой-то слушатель, который динамически загружает следующий фрагмент repeatingElements, чтобы передать его маршаллеру, прежде чем он напишет заключительный тег rootElement. Но как это сделать? До сих пор я использовал JAXB для сортировки небольших файлов, и в документации JAXB не было много намеков на этот случай использования.
Ответы
Ответ 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.