我面临的问题是如何将大量对象编组到一个XML文件中,如此之大,我无法一步完成整个列表的编组。我有一个方法以块的形式返回这些对象,但随后我使用JAXB编组它们,marshaller返回一个例外,即这些对象不是根元素。对于您希望在一个步骤中编组完整文档的正常情况,这是正常的,但如果我将JAXB_FRAGMENT属性设置为true,也会发生这种情况。
这是所需的XML输出:
<rootElem>
<startDescription></startDescription>
<repeatingElem></repeatingElem>
<repeatingElem></repeatingElem>...
</rootElem>
所以我假设我需要某种类型的侦听器来动态加载下一个重复元素块,以便在编写rootElement的结束标记之前将其提供给编组器。但是怎么做呢?到目前为止,我只使用JAXB来编组小文件,而JAXB文档没有提供该用例的大量提示。
答案 0 :(得分:19)
我知道这是一个老问题,但我在搜索另一个类似问题的重复时遇到了它。
正如@skaffman建议的那样,你想要在启用JAXB_FRAGMENT
的情况下使用Marshal并将你的对象包装在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();
}
}
答案 1 :(得分:9)
正如您所发现的,如果某个类没有@XmlRootElement
注释,那么您就无法将该类的实例传递给编组器。但是,有一个简单的方法 - 将对象包装在JAXBElement
中,然后将其传递给编组程序。
现在JAXBElement
是一个相当笨拙的野兽,但它所做的是包含你要编组的对象的元素名称和命名空间,这些信息通常包含在@XmlRootElement
注释中。只要您拥有名称和命名空间,就可以构造一个JAXBElement
来包装您的POJO,然后封送它。
如果您的POJO是由XJC生成的,那么它也会生成一个ObjectFactory
类,其中包含用于为您构建JAXBElement
包装器的工厂方法,使事情变得更容易。
你仍然必须使用JAXB_FRAGMENT
属性来重复内部元素,否则JAXB每次都会产生类似XML prolog的东西,这是你不想要的。
答案 2 :(得分:-10)
我对JAXB知之甚少,所以我无能为力。但如果你不介意,我有一个建议。
编写XML比阅读它要容易得多,因此解决问题的方法可能是使用更“低级”的方法。只需使用一个可用的XML开源库编写自己的marshaller。我认为你可以使用dom4j轻松做你想做的事。