【问题标题】:JAXB marshals XML differently to OutputStream vs. StringWriterJAXB 以与 OutputStream 和 StringWriter 不同的方式编组 XML
【发布时间】:2010-06-11 14:38:02
【问题描述】:

如果此问题已得到解答,我深表歉意,但我一直在使用的搜索词(即 JAXB @XmlAttribute condensedJAXB XML marshal to字符串不同的结果)没有提出任何建议。

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

<element value="VALUE"></element>

当编组为字符串时,它们是:

<element value="VALUE"/>

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

/** 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)的结果如下:

输出到文件

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
<ns2:container xmlns:ns2="project/schema">  
    <number>1</number>  
    <word>stackoverflow</word>  
    <data value="This is internal"></data>  
</ns2:container>

输出到字符串

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>  
<ns2:container xmlns:ns2="project/schema">  
    <number>1</number>  
    <word>stackoverflow</word>  
    <data value="This is internal"/>  
</ns2:container>

【问题讨论】:

  • 感谢您稍微清理一下,我无法解释 XML。
  • 进一步调查表明,我正在寻找的是控制 JAXB 是否以 HTML(成对标签)或 XML(单标签)样式编写“空元素”的能力。在像 JaxMe 这样的其他 JAXB 提供程序中,这是一个可以设置的属性,但显然不是在 JAXB 中。我只是想知道为什么它会根据目的地类型而有所不同。我知道它们在功能上是等效的,但我需要它们相同才能使用我们的系统。有什么想法吗?

标签: java xml jaxb java-metro-framework


【解决方案1】:

看起来这可能是 JAXB 中的一个“错误”。查看源代码,对 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-ri\runtime\src\com\sun\xml\bind\v2\runtime\MarshallerImpl.java.

您正在创建的两个作家是:

jaxb-ri\runtime\src\com\sun\xml\bind\v2\runtime\output\UTF8XmlOutput.java

jaxb-ri\runtime\src\com\sun\xml\bind\v2\runtime\output\XMLStreamWriterOutput.java

【讨论】:

  • 更准确地说,这是 Metro JAXB 实现中的一个错误。 EclipseLink JAXB (MOXy) 等其他 JAXB 实现没有此错误。
【解决方案2】:

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

【讨论】:

  • 确实,因为该链接现已失效
【解决方案3】:

我不知道为什么 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,以尽量保持输出简短。听起来不太可能,但我没有其他解释。

【讨论】:

  • 在返回此处并获得一致的输出之前,我最终完全做到了这一点。谢谢。
猜你喜欢
  • 2016-04-23
  • 2012-03-13
  • 2019-10-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-27
  • 1970-01-01
  • 2014-10-27
相关资源
最近更新 更多