如何使用有时包含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.
了解更多信息
- http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
- http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
注 – 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
工作对我来说很完美!