java 如何使用 JAXB 编组有时包含 XML 内容而有时不包含的字符串?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12428727/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to marshall a string using JAXB that sometimes contains XML content and sometimes does not?
提问by CodeBlue
Consider this example -
考虑这个例子 -
I have a class called Report that has a field of type Message. The Message class has a field called "body" which is a string. "body" can be any string, but sometimes it contains properly formatted XML content. How can I ensure that when the "body" contains XML content, the serialization takes the form of an XML structure rather than what it gives at present?
我有一个名为 Report 的类,它有一个类型为 Message 的字段。Message 类有一个名为“body”的字段,它是一个字符串。“body”可以是任何字符串,但有时它包含格式正确的 XML 内容。如何确保当“主体”包含 XML 内容时,序列化采用 XML 结构的形式而不是它目前给出的形式?
Here is the code with the output -
这是带有输出的代码 -
Reportclass
报告类
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; }
}
Messageclass
留言类
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; }
}
Main
主要的
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);
}
}
The output is as follows -
输出如下 -
<?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>
As you can see in the above output, for the second instance of "body", the serialization produced
正如你在上面的输出中看到的,对于“body”的第二个实例,序列化产生
<body><rootTag><body>All systems online.</body></rootTag></body>
instead of
代替
<body><rootTag><body>All systems online.</body></rootTag></body>
How to solve this problem?
如何解决这个问题呢?
回答by bdoughan
Note:I'm the EclipseLink JAXB (MOXy)lead and a member of the JAXB (JSR-222)expert group.
注意:我是EclipseLink JAXB (MOXy) 的负责人,也是JAXB (JSR-222)专家组的成员。
This use case is mapped using the @XmlAnyElement
annotation and specifying a DOMHandler
. There appears to be bug when doing this with the JAXB RI, but the following use case works with EclipseLink JAXB (MOXy).
此用例使用@XmlAnyElement
注解并指定一个DOMHandler
. 使用 JAXB RI 执行此操作时似乎存在错误,但以下用例适用于 EclipseLink JAXB (MOXy)。
BodyDomHandler
BodyDomHandler
By default a JAXB impleemntation will represent unmapped content as a DOM node. You can leverage a DomHandler
to an alternate representation of the DOM, In this case we will represent the DOM as a String
.
默认情况下,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<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
信息
Below is how you would specify the @XmlAnyElement
annotation on your Message
class.
以下是您@XmlAnyElement
在Message
类上指定注释的方法。
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; }
}
Output
输出
Below is the output from running your SerialziationTest
:
以下是运行您的输出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>
For More Information
想要查询更多的信息
- http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
- http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
- http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
- http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
NOTE - Bug in JAXB RI
注意 - JAXB RI 中的错误
There appears to be a bug in the JAXB reference implementation, and the example code will result in a stack trace like the following:
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
回答by Lokesh
If its only for Marshalling, and to ignore the < and >, We can use the following:
如果它仅用于编组,而忽略 < 和 >,我们可以使用以下内容:
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);
}
});
回答by skay
3 different solutions 1), 2) 3), here below :
3 种不同的解决方案 1), 2) 3),如下:
1) Following post is a the description of your solution Loresh :
1)以下帖子是对您的解决方案 Loresh 的描述:
http://anna-safronova.livejournal.com/2524.html?thread=9180
http://anna-safronova.livejournal.com/2524.html?thread=9180
This is still missing limitations details.
这仍然缺少限制细节。
With embeeded html, we need a
<![CDATA
blockJAXB's dependancy
使用嵌入的 html,我们需要一个
<![CDATA
块JAXB 的依赖
com.sun.xml.bind.marshaller.CharacterEscapeHandler
com.sun.xml.bind.marshaller.CharacterEscapeHandler
Needs import jaxb-impl for compilation / and may be required for excution, e.g.
需要导入 jaxb-impl 进行编译 / 并且可能需要执行,例如
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.4</version>
- Limitation : this solution is Container-specific and may not run because of class-loading policy.
- 限制:此解决方案是特定于容器的,可能由于类加载策略而无法运行。
2) Another similar approach is JDK's rt.jar dependancy
2)另一种类似的做法是JDK的rt.jar依赖
com.sun.xml.internal.bind.CharacterEscapeHandler
com.sun.xml.internal.bind.CharacterEscapeHandler
http://theopentutorials.com/tutorials/java/jaxb/jaxb-marshalling-and-unmarshalling-cdata-block/
http://theopentutorials.com/tutorials/java/jaxb/jaxb-marshalling-and-unmarshalling-cdata-block/
Same limitation / dependends on target JDK, and some tweaks on Eclipse/Maven are necessary (bad alternative / My opinion)
相同的限制/依赖于目标 JDK,并且需要对 Eclipse/Maven 进行一些调整(糟糕的替代方案/我的意见)
3) Finally, the best solution was found on another post of Reg Whitton :
3) 最后,在 Reg Whitton 的另一篇文章中找到了最佳解决方案:
https://stackoverflow.com/a/12637295/560410
https://stackoverflow.com/a/12637295/560410
and this is the detailed reciepe :
这是详细的食谱:
http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html
http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html
Worked perfect for me !
非常适合我!