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

如何使用有时包含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>&lt;rootTag&gt;&lt;body&gt;All systems online.&lt;/body&gt;&lt;/rootTag&gt;</body> 
    </message> 
</Report> 

正如可以在上面的输出参见,“主体”的第二个实例中,串行化产生

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

更多信息

注 - 错误的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 
+0

这样做后,我得到一个异常说 - “无法编组类型”java.lang.String“作为一个元素,因为它缺少@XmlRootElement注释” – CodeBlue

+0

@CodeBlue - 是似乎有一个错误在本例中的JAXB(JSR-222)实现。我使用EclipseLink JAXB(MOXy)将示例放在一起。 –

+1

哇。为什么他们没有修好它? – CodeBlue

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

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); 
        } 
       }); 
+0

没有这样的属性: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

对我来说工作很完美!