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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 08:51:23  来源:igfitidea点击:

How to marshall a string using JAXB that sometimes contains XML content and sometimes does not?

javaserializationjaxb

提问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>&lt;rootTag&gt;&lt;body&gt;All systems online.&lt;/body&gt;&lt;/rootTag&gt;</body>
    </message>
</Report>

As you can see in the above output, for the second instance of "body", the serialization produced

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

 <body>&lt;rootTag&gt;&lt;body&gt;All systems online.&lt;/body&gt;&lt;/rootTag&gt;</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 @XmlAnyElementannotation 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 DomHandlerto an alternate representation of the DOM, In this case we will represent the DOM as a String.

默认情况下,JAXB 实现会将未映射的内容表示为 DOM 节点。您可以利用DomHandlerDOM 的替代表示,在这种情况下,我们将 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 @XmlAnyElementannotation on your Messageclass.

以下是您@XmlAnyElementMessage类上指定注释的方法。

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

想要查询更多的信息

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 <![CDATAblock

  • JAXB'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 !

非常适合我!