使用JAXB和XMLStreamWriter编写空标记

我正在使用标准的JAXB实现。
这是我的带注释的类(是一组类的一部分)

@XmlType() @XmlAccessorType(FIELD) class MyClass { @XmlValue protected final String value = null; @XmlAttribute protected String attr; ...get/set for attr... } 

我正在编写root对象

 JaxbContext ctx = JAXBContext.newInstance("path.to.package"); XMLStreamWriter writer = new IndentingXMLStreamWriter(file); //stax-utils writer ctx.marshal(rootObject, writer); 

结果是但我需要空标记为 。 我尝试了编写器和目标流的一些不同组合(并读取了几十个SO问题)(不仅是文件,还有StringWriter和其他人,但我需要将对象保存到文件中)。 任何建议或解决方案?

这是编组到XMLStreamWriter时可能出现的问题。 如果您可以编组到FileFileOutputStreamFileWriter以获取您正在寻找的行为。

注意:

要使用这些输出目标格式化输出,您可以在Marshaller上设置以下属性:

 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 

我解决了使用自定义XMLStreamWriter,如果tag-name根本没有内容,则用替换 。 背后的想法是在startElement-endElement之间推迟写事件。 当找到endElement时 ,如果不是边界之间的内容事件, 则将 startElement替换为emptyElement根本禁止endElement 。 对不起评论不好,我稍后会回来让它更容易理解。

 public class EmptyTagXMLStreamWriter extends StreamWriterDelegate { class Event { Method m; Object[] args; } enum EventEnum { writeStartElement, writeAttribute, writeNamespace, writeEndElement, setPrefix, setDefaultNamespace, } private List queue = new ArrayList(); protected EmptyTagXMLStreamWriter(XMLStreamWriter out) { super(out); } @Override public void writeStartElement(String localName) throws XMLStreamException { d(e(m("writeStartElement",String.class)), localName); } @Override public void writeStartElement(String namespaceURI, String localName) throws XMLStreamException { d(e(m("writeStartElement",String.class,String.class)), namespaceURI, localName); } @Override public void writeStartElement(String prefix, String localName, String namespaceURI) throws XMLStreamException { d(e(m("writeStartElement",String.class,String.class,String.class)), prefix, localName, namespaceURI); } @Override public void writeAttribute(String localName, String value) throws XMLStreamException { d(e(m("writeAttribute",String.class, String.class)), localName, value); } @Override public void writeAttribute(String namespaceURI, String localName, String value) throws XMLStreamException { d(e(m("writeAttribute",String.class, String.class, String.class)), namespaceURI, localName, value); } @Override public void writeAttribute(String prefix, String namespaceURI, String localName, String value) throws XMLStreamException { d(e(m("writeAttribute",String.class, String.class, String.class, String.class)), prefix, namespaceURI, localName, value); } @Override public void writeCData(String data) throws XMLStreamException { fq(); super.writeCData(data); } @Override public void writeCharacters(char[] text, int start, int len) throws XMLStreamException { fq(); super.writeCharacters(text, start, len); } @Override public void writeCharacters(String text) throws XMLStreamException { fq(); super.writeCharacters(text); } @Override public void writeComment(String data) throws XMLStreamException { fq(); super.writeComment(data); } @Override public void writeDTD(String dtd) throws XMLStreamException { fq(); super.writeDTD(dtd); } @Override public void writeProcessingInstruction(String target) throws XMLStreamException { fq(); super.writeProcessingInstruction(target); } @Override public void writeProcessingInstruction(String target, String data) throws XMLStreamException { fq(); super.writeProcessingInstruction(target, data); } @Override public void writeNamespace(String prefix, String namespaceURI) throws XMLStreamException { d(e(m("writeNamespace",String.class, String.class)), prefix, namespaceURI); } @Override public void writeEndElement() throws XMLStreamException { d(e(m("writeEndElement"))); } @Override public void writeEndDocument() throws XMLStreamException { fq(); super.writeEndDocument(); } @Override public void writeDefaultNamespace(String namespaceURI) throws XMLStreamException { super.writeDefaultNamespace(namespaceURI); } @Override public void flush() throws XMLStreamException { if(queue.isEmpty()) super.flush(); } @Override public void close() throws XMLStreamException { fq(); out.close(); } @Override public void setPrefix(String prefix, String uri) throws XMLStreamException { d(e(m("setPrefix", String.class, String.class)), prefix, uri); } @Override public void setDefaultNamespace(String uri) throws XMLStreamException { d(e(m("setDefaultNamespace", String.class)), uri); } void d(Event e,Object...args) throws XMLStreamException { e.args = args; switch(EventEnum.valueOf(emgetName())) { case writeStartElement: fq(); queue.add(e); break; case writeAttribute: case writeNamespace: case setPrefix: case setDefaultNamespace: if(!queue.isEmpty()) queue.add(e); else ex(e, args); break; case writeEndElement: if(!queue.isEmpty()) { final Event e1 = queue.get(0); e1.m = m("writeEmptyElement", e1.m.getParameterTypes()); fq(); } else { ex(e, args); } break; } } Event e(Method m,Object...params) { final Event e = new Event(); em = m; e.args = params; return e; } Method m(String methodName,Class...args) throws XMLStreamException { try { return XMLStreamWriter.class.getMethod(methodName, args); } catch (Exception e) { throw new XMLStreamException(e); } } void fq() throws XMLStreamException { for(int i = 0;i < queue.size();i++) { Event e = queue.get(i); ex(e, e.args); } queue.clear(); } void ex(Event e,Object...args) throws XMLStreamException { try { eminvoke(super.out, e.args); } catch(Exception ex) { throw new XMLStreamException(ex); } } }