JAXB definitionsMarshalling is the process of transforming Java objects into XML documents. Unmarshalling is the process of reading XML documents into Java objects.
Marshalling is converting the data present in an object into a an xml format and viewing it in an xml format and unmarshalling is reverse of it converting an xml file into an object.
In JAXB, marshalling involves parsing an XML content object tree and writing out an XML document that is an accurate representation of the original XML document, and is valid with respect the source schema. JAXB can marshal XML data to XML documents, SAX content handlers, and DOM nodes.
To unmarshal an xml string into a JAXB object, you will need to create an Unmarshaller from the JAXBContext, then call the unmarshal() method with a source/reader and the expected root object.
You could do the following:
AdapterCDATA
package forum14193944;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class AdapterCDATA extends XmlAdapter<String, String> {
@Override
public String marshal(String arg0) throws Exception {
return "<![CDATA[" + arg0 + "]]>";
}
@Override
public String unmarshal(String arg0) throws Exception {
return arg0;
}
}
Root
The @XmlJavaTypeAdapter
annotation is used to specify that the XmlAdapter
should be used.
package forum14193944;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
@XmlJavaTypeAdapter(AdapterCDATA.class)
private String name;
@XmlJavaTypeAdapter(AdapterCDATA.class)
private String surname;
@XmlJavaTypeAdapter(AdapterCDATA.class)
private String id;
}
Demo
I had to wrap System.out
in an OutputStreamWriter
to get the desired effect. Also note that setting a CharacterEscapeHandler
means that it is responsible for all escape handling for that Marshaller
.
package forum14193944;
import java.io.*;
import javax.xml.bind.*;
import com.sun.xml.bind.marshaller.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14193944/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new CharacterEscapeHandler() {
@Override
public void escape(char[] ac, int i, int j, boolean flag,
Writer writer) throws IOException {
writer.write(ac, i, j);
}
});
marshaller.marshal(root, new OutputStreamWriter(System.out));
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<name><![CDATA[<h1>kshitij</h1>]]></name>
<surname><![CDATA[<h1>solanki</h1>]]></surname>
<id><![CDATA[0]]></id>
</root>
Please Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
If you use MOXy as your JAXB (JSR-222) provider then you can leverage the @XmlCDATA
extension for your use case.
Root
The @XmlCDATA
annotation is used to indicate that you want the contents of a field/property wrapped in a CDATA section. The @XmlCDATA
annotation can be used in combination with @XmlElement
.
package forum14193944;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlCDATA;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
@XmlCDATA
private String name;
@XmlCDATA
private String surname;
@XmlCDATA
private String id;
}
jaxb.properties
To use MOXy as your JAXB provider you need to add file named jaxb.properties
with the following entry.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
Below is some demo code to prove that everything works.
package forum14193944;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14193944/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
Below is the input to and output from running the demo code.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<name><![CDATA[<h1>kshitij</h1>]]></name>
<surname><![CDATA[<h1>solanki</h1>]]></surname>
<id><![CDATA[0]]></id>
</root>
For More Information
Sorry for digging out this question, and posting a new answer (my rep isn't high enough yet to comment...). I ran into the same issue, I tried Blaise Doughan's answer, but from my tests, either it doesn't cover all cases, either I'm doing something wrong somewhere.
marshaller.setProperty(CharacterEscapeHandler.class.getName(),
new CharacterEscapeHandler() {
@Override
public void escape(char[] ac, int i, int j, boolean flag,
Writer writer) throws IOException {
writer.write(ac, i, j);
}
});
From my tests, this code removes all escaping, no matter if you are using the @XmlJavaTypeAdapter(AdapterCDATA.class)
annotation on your attribute...
To fix that issue, I implemented the following CharacterEscapeHandler
:
public class CDataAwareUtfEncodedXmlCharacterEscapeHandler implements CharacterEscapeHandler { private static final char[] cDataPrefix = "<![CDATA[".toCharArray(); private static final char[] cDataSuffix = "]]>".toCharArray(); public static final CDataAwareUtfEncodedXmlCharacterEscapeHandler instance = new CDataAwareUtfEncodedXmlCharacterEscapeHandler(); private CDataAwareUtfEncodedXmlCharacterEscapeHandler() { } @Override public void escape(char[] ch, int start, int length, boolean isAttVal, Writer out) throws IOException { boolean isCData = length > cDataPrefix.length + cDataSuffix.length; if (isCData) { for (int i = 0, j = start; i < cDataPrefix.length; ++i, ++j) { if (cDataPrefix[i] != ch[j]) { isCData = false; break; } } if (isCData) { for (int i = cDataSuffix.length - 1, j = start + length - 1; i >= 0; --i, --j) { if (cDataSuffix[i] != ch[j]) { isCData = false; break; } } } } if (isCData) { out.write(ch, start, length); } else { MinimumEscapeHandler.theInstance.escape(ch, start, length, isAttVal, out); } } }
If your encoding is not UTF*, you may not want to call MinimumEscapeHandler but rather NioEscapeHandler or even DumbEscapeHandler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With