JAXB以不同的方式将XML封送到OutputStream和StringWriter

我很抱歉,如果这已经得到解答,但我一直在使用的搜索术语(即JAXB @XmlAttribute压缩JAXB XML编组到String不同的结果 )没有提出任何建议。

我正在使用JAXB取消/编组使用@XmlElement@XmlAttribute注释注释的对象。 我有一个格式化程序类,它提供了两个方法 – 一个包装marshal方法并接受对象编组和一个OutputStream ,另一个只接受对象并将XML输出作为String返回。 不幸的是,这些方法不能为相同的对象提供相同的输出。 封送到文件时,内部标有@XmlAttribute简单对象字段打印为:

  

当编组到一个字符串时,它们是:

  

我更倾向于两种情况的第二种格式,但我很好奇如何控制差异,并且无论如何都会满足于它们。 我甚至创建了一个静态编组器,两种方法都使用它来消除不同的实例值。 格式代码如下:

 /** Marker interface for classes which are listed in jaxb.index */ public interface Marshalable {} 

 /** Local exception class */ public class XMLMarshalException extends BaseException {} 

 /** Class which un/marshals objects to XML */ public class XmlFormatter { private static Marshaller marshaller = null; private static Unmarshaller unmarshaller = null; static { try { JAXBContext context = JAXBContext.newInstance("path.to.package"); marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8"); unmarshaller = context.createUnmarshaller(); } catch (JAXBException e) { throw new RuntimeException("There was a problem creating a JAXBContext object for formatting the object to XML."); } } public void marshal(Marshalable obj, OutputStream os) throws XMLMarshalException { try { marshaller.marshal(obj, os); } catch (JAXBException jaxbe) { throw new XMLMarshalException(jaxbe); } } public String marshalToString(Marshalable obj) throws XMLMarshalException { try { StringWriter sw = new StringWriter(); return marshaller.marshal(obj, sw); } catch (JAXBException jaxbe) { throw new XMLMarshalException(jaxbe); } } } 

 /** Example data */ @XmlType @XmlAccessorType(XmlAccessType.FIELD) public class Data { @XmlAttribute(name = value) private String internalString; } 

 /** Example POJO */ @XmlType @XmlRootElement(namespace = "project/schema") @XmlAccessorType(XmlAccessType.FIELD) public class Container implements Marshalable { @XmlElement(required = false, nillable = true) private int number; @XmlElement(required = false, nillable = true) private String word; @XmlElement(required = false, nillable = true) private Data data; } 

调用marshal(container, new FileOutputStream("output.xml"))marshalToString(container)结果如下:

输出到文件

   1 stackoverflow   

输出到字符串

   1 stackoverflow   

看起来这可能是JAXB中的“bug”。 查看源代码,调用marshal()会根据输出/编写器类型参数创建不同的编写器:

 public void marshal(Object obj, OutputStream out, NamespaceContext inscopeNamespace) throws JAXBException { write(obj, createWriter(out), new StAXPostInitAction(inscopeNamespace,serializer)); } public void marshal(Object obj, XMLStreamWriter writer) throws JAXBException { write(obj, XMLStreamWriterOutput.create(writer,context), new StAXPostInitAction(writer,serializer)); } 

关于它们如何处理“空元素”,编写器的实现是不同的。 以上代码来自:

JAXB里\运行的\ src \ COM \太阳\ XML \绑定\ V2 \运行\ MarshallerImpl.java。

你正在创作的两位作家是:

JAXB里\运行的\ src \ COM \太阳\ XML \绑定\ V2 \运行\输出\ UTF8XmlOutput.java

JAXB里\运行的\ src \ COM \太阳\ XML \绑定\ V2 \运行\输出\ XMLStreamWriterOutput.java

好消息是JAXB是一个具有多个实现的规范(就像JPA一样)。 如果一个实现不满足您的需求,则可以使用其他实现,例如EclipseLink JAXB(MOXy):

我不知道为什么JAXB这样做 – 或者即使它是JAXB – 例如,如果JAXB通过SAXContentHandler输出XML,那么它无法直接控制标签的生成方式。

要获得一致的行为,可以将OutputStream包装在OutputStreamWriter中,例如

  public void marshal(Marshalable obj, OutputStream os) throws XMLMarshalException { try { marshaller.marshal(obj, new OutputStreamWriter(os, "UTF-8")); } catch (JAXBException jaxbe) { throw new XMLMarshalException(jaxbe); } } 

同样,如果将StringWriter包装在PrintWriter中,您可能会看到会发生什么。 也许有一些自定义代码可以检测StringWriter以尽可能地缩短输出。 听起来不太可能,但我没有其他解释。

为什么这有关系? 等同于xml中的