如何使用有时包含XML内容且有时不包含XML内容的JAXB编组字符串?
考虑这个例子 -如何使用有时包含XML内容且有时不包含XML内容的JAXB编组字符串?
我有一个叫做Report的类,它有一个Message类型的字段。 Message类有一个名为“body”的字段,它是一个字符串。 “body”可以是任何字符串,,但有时它包含格式正确的XML内容。我如何确保当“body”包含XML内容时,序列化采用XML结构的形式,而不是它目前提供的内容?
这里是与输出代码 -
报告类
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("<rootTag><body>All systems online.</body></rootTag>");
report.setMessage(message);
jaxbMarshaller.marshal(report, System.out);
}
}
的输出是如下 -
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Report>
<message>
<body>Sample report message.</body>
</message>
</Report>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Report>
<message>
<body><rootTag><body>All systems online.</body></rootTag></body>
</message>
</Report>
正如可以在上面的输出参见,“主体”的第二个实例中,串行化产生
<body><rootTag><body>All systems online.</body></rootTag></body>
代替
<body><rootTag><body>All systems online.</body></rootTag></body>
如何解决这个问题?
备注:我是EclipseLink JAXB (MOXy)的领导者和JAXB (JSR-222)专家组的成员。
该使用案例使用@XmlAnyElement
注释和指定DOMHandler
进行映射。使用JAXB RI进行此操作时似乎存在错误,但以下用例适用于EclipseLink JAXB(MOXy)。
BodyDomHandler
缺省情况下JAXB impleemntation将代表未映射的内容作为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<String, StreamResult> {
private static final String BODY_START_TAG = "<body>";
private static final String BODY_END_TAG = "</body>";
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
输出:
<?xml version="1.0" encoding="UTF-8"?>
<Report>
<message>
<body>Sample report message.</body>
</message>
</Report>
<?xml version="1.0" encoding="UTF-8"?>
<Report>
<message>
<body>
<rootTag>
<body>All systems online.</body>
</rootTag>
</body>
</message>
</Report>
更多信息
- 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);
}
});
没有这样的属性:javax.xml.bind.PropertyException:name:com.sun.xml.bind.marshaller.CharacterEscapeHandler ... – Sllouyssgort
3个不同的解决方案1),2)3),在这里如下:
1)下面的帖子是您的解决方案Loresh的描述:
http://anna-safronova.livejournal.com/2524.html?thread=9180
这仍然是缺少限制细节。
随着embeeded HTML,我们需要一个
<![CDATA
块JAXB的扶养
com.sun.xml.bind.marshaller.CharacterEscapeHandler
需要导入JAXB的IMPL用于编译/并且可能需要用于编译,例如
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.4</version>
- 限制:该解决方案是集装箱专用,不得因为类加载政策的运行。
2)另一种类似的方法是JDK的rt.jar中扶养
com.sun.xml.internal.bind。CharacterEscapeHandler
http://theopentutorials.com/tutorials/java/jaxb/jaxb-marshalling-and-unmarshalling-cdata-block/
同样的限制/目标JDK dependends,和基于Eclipse/Maven的一些调整是必要的(错误的选择/我的看法)
3)最后,最好的解决办法是在另一篇文章中找到注册惠顿的:
https://*.com/a/12637295/560410
,这是详细reciepe:
http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html
对我来说工作很完美!
这样做后,我得到一个异常说 - “无法编组类型”java.lang.String“作为一个元素,因为它缺少@XmlRootElement注释” – CodeBlue
@CodeBlue - 是似乎有一个错误在本例中的JAXB(JSR-222)实现。我使用EclipseLink JAXB(MOXy)将示例放在一起。 –
哇。为什么他们没有修好它? – CodeBlue