How to sort a string using JAXB, which sometimes contains XML content and sometimes not?

Consider this example -

I have a class called Report that has a field of type Message. The Message class has a body field, which is a string. A body can be any string, but sometimes it contains properly formatted XML content . How can I make sure that when the "body" contains XML content, serialization takes the form of an XML structure, rather than what it currently provides?

Here is the exit code -

Report class

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; } } 

Post class

 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; } } 

home

 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 can be seen from the above, for the second instance of "body" the serialization performed

  <body>&lt;rootTag&gt;&lt;body&gt;All systems online.&lt;/body&gt;&lt;/rootTag&gt;</body> 

instead

 <body><rootTag><body>All systems online.</body></rootTag></body> 

How to solve this problem?

+4
source share
3 answers

Note. I am an EclipseLink JAXB (MOXy) and a member of the JAXB Group (JSR-222) .

This usage example is displayed using the @XmlAnyElement annotation and specifying a DOMHandler . It seems that the error occurs when using JAXB RI, but the following usage example works with EclipseLink JAXB (MOXy).

BodyDomHandler

By default, a JAXB implementation will represent unlabeled content as a DOM node. You can use the DOMHandler for an alternative representation of the DOM, in which case we will represent the DOM as a 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 you can specify the @XmlAnyElement annotation in your Message class.

 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

The following is the result of running 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> 

Additional Information

NOTE. - Error in JAXB RI

There seems to be an error in the JAXB reference implementation, and the sample code will lead to a stack trace, as shown below:

 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 
+7
source

If it is only for Marshalling and ignore <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); } }); 
+2
source

3 different solutions 1), 2) 3), below:

1) The following message is a description of your solution to Loresh:

http://anna-safronova.livejournal.com/2524.html?thread=9180

This is still missing restriction information.

  • With embedded html we need the <![CDATA block

  • JAXB dependency

com.sun.xml.bind.marshaller.CharacterEscapeHandler

A jaxb-impl import is required to compile / and may be required to extract, for example.

 <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId> <version>2.2.4</version> 
  • Limitation: this solution is container specific and may not be executed due to the class loading policy.

2) Another similar approach is the JDK rt.jar dependency

com.sun.xml.internal.bind.CharacterEscapeHandler

http://theopentutorials.com/tutorials/java/jaxb/jaxb-marshalling-and-unmarshalling-cdata-block/

The same restrictions / dependencies on the target JDK and some settings on Eclipse / Maven are necessary (poor alternative / My opinion)

3) Finally, the best solution was found on another Reg Whitton post:

fooobar.com/questions/138304 / ...

and this is a detailed reciepe:

http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html

Worked perfect for me!

+2
source

Source: https://habr.com/ru/post/1434258/


All Articles