如何使用有时包含XML内容的JAXB来编组字符串,有时却不包含?

考虑这个例子 –

我有一个名为Report的类,它有一个Message类型的字段。 Message类有一个名为“body”的字段,它是一个字符串。 “body”可以是任何字符串, 但有时它包含格式正确的XML内容 。 如何确保当“body”包含XML内容时,序列化采用XML结构的forms而不是目前提供的forms?

这是输出的代码 –

报告

import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlRootElement(name = "Report") @XmlType(propOrder = { "message"}) public class Report { private Message message; public Message getMessage() { return message; } public void setMessage(Message m) { message = m; } } 

消息

 import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlType; @XmlType(propOrder = { "body" }) public class Message { private String body; public String getBody() { return body; } @XmlElement public void setBody(String body) { this.body = body; } } 

主要

 import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; public class SerializationTest { public static void main(String args[]) throws Exception { JAXBContext jaxbContext = JAXBContext.newInstance(Report.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); Report report = new Report(); Message message = new Message(); message.setBody("Sample report message."); report.setMessage(message); jaxbMarshaller.marshal(report, System.out); message.setBody("All systems online."); report.setMessage(message); jaxbMarshaller.marshal(report, System.out); } } 

输出如下 –

    Sample report message.      <rootTag><body>All systems online.</body></rootTag>   

正如您在上面的输出中所看到的,对于“body”的第二个实例,生成了序列化

  <rootTag><body>All systems online.</body></rootTag> 

代替

 All systems online. 

如何解决这个问题呢?

注意:我是EclipseLink JAXB(MOXy)的负责人,也是JAXB(JSR-222)专家组的成员。

使用@XmlAnyElement批注并指定DOMHandler来映射此用例。 使用JAXB RI执行此操作时似乎存在错误,但以下用例适用于EclipseLink JAXB(MOXy)。

BodyDomHandler

默认情况下,JAXB实现将未映射的内容表示为DOM节点。 您可以将DomHandler用于DOM的替代表示,在这种情况下,我们将DOM表示为String

 import java.io.*; import javax.xml.bind.ValidationEventHandler; import javax.xml.bind.annotation.DomHandler; import javax.xml.transform.Source; import javax.xml.transform.stream.*; public class BodyDomHandler implements DomHandler { private static final String BODY_START_TAG = ""; private static final String BODY_END_TAG = ""; private StringWriter xmlWriter = new StringWriter(); public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) { return new StreamResult(xmlWriter); } public String getElement(StreamResult rt) { String xml = rt.getWriter().toString(); int beginIndex = xml.indexOf(BODY_START_TAG) + BODY_START_TAG.length(); int endIndex = xml.indexOf(BODY_END_TAG); return xml.substring(beginIndex, endIndex); } public Source marshal(String n, ValidationEventHandler errorHandler) { try { String xml = BODY_START_TAG + n.trim() + BODY_END_TAG; StringReader xmlReader = new StringReader(xml); return new StreamSource(xmlReader); } catch(Exception e) { throw new RuntimeException(e); } } } 

信息

下面是如何在Message类上指定@XmlAnyElement注释的方法。

 import javax.xml.bind.annotation.XmlAnyElement; import javax.xml.bind.annotation.XmlType; @XmlType(propOrder = { "body" }) public class Message { private String body; public String getBody() { return body; } @XmlAnyElement(BodyDomHandler.class) public void setBody(String body) { this.body = body; } } 

产量

以下是运行SerialziationTest的输出:

    Sample report message.        All systems online.     

了解更多信息

注 – JAXB RI中的错误

JAXB参考实现中似乎存在错误,示例代码将导致堆栈跟踪,如下所示:

 Exception in thread "main" javax.xml.bind.MarshalException - with linked exception: [com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an @XmlRootElement annotation] at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:317) at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:243) at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:75) at forum12428727.SerializationTest.main(SerializationTest.java:20) Caused by: com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an @XmlRootElement annotation at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:216) at com.sun.xml.internal.bind.v2.runtime.LeafBeanInfoImpl.serializeRoot(LeafBeanInfoImpl.java:126) at com.sun.xml.internal.bind.v2.runtime.property.SingleReferenceNodeProperty.serializeBody(SingleReferenceNodeProperty.java:100) at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:306) at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:664) at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:141) at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:306) at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:561) at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:290) at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:462) at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:314) ... 3 more 

如果仅用于编组,并忽略<和>,我们可以使用以下内容:

 marshaller.setProperty("com.sun.xml.bind.marshaller.CharacterEscapeHandler", new CharacterEscapeHandler() { @Override public void escape(char[] ac, int i, int j, boolean flag, Writer writer) throws IOException { writer.write(ac, i, j); } }); 

3种不同的解决方案1),2)3),如下:

1)以下post是你的解决方案Loresh的描述:

http://anna-safronova.livejournal.com/2524.html?thread=9180

这仍然缺少限制细节。

  • 使用embeeded html,我们需要一个块]

  • JAXB的依赖性

com.sun.xml.bind.marshaller.CharacterEscapeHandler

需要导入jaxb-impl进行编译/并且可能需要执行,例如

 com.sun.xml.bind jaxb-impl 2.2.4 
  • 限制:此解决方案是特定于Container的,并且可能因类加载策略而无法运行。

2)另一种类似的方法是JDK的rt.jar依赖性

com.sun.xml.internal.bind.CharacterEscapeHandler

http://theopentutorials.com/tutorials/java/jaxb/jaxb-marshalling-and-unmarshalling-cdata-block/

相同的限制/取决于目标JDK,Eclipse / Maven上的一些调整是必要的(糟糕的替代/我的意见)

3)最后,在Reg Whitton的另一篇文章中找到了最佳解决方案:

https://stackoverflow.com/a/12637295/560410

这是详细的reciepe:

http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html

工作对我来说很完美!