The essence of question briefly:
1) How to pun one xml, which should be wrapped by CDATA, inside other xml in Jersey?
2) How to avoid converting "<" into "& lt;" and ">" into "& gt;" during Marshalling?
More details:
I need to send XML to external server
This XML should be in the following format:
<root>
<element1>some value</element1>
<element2>
<![CDATA[<?xml version="1.0" encoding="UTF-8" ?>
<innerXlmElement>
<item>
<itemElement1>1</itemElement1>
<itemElement2>1</itemElement2>
</item>
...
<item>
<itemElement1>n</itemElement1>
<itemElement2>n</itemElement2>
</item>
</innerXlmElement>]]>
</element2>
</root>
That is one of the elements inside of external XML should be another XML, wrapped by CDATA.
First of all I made following bean:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "root")
public class Req {
protected String element1;
protected Element2 element2;
@XmlElement(name = "element1")
public String getElement1() {
return element1;
}
public void setElement1(String element1) {
this.element1 = element1;
}
@XmlElement(name = "element2")
public Element2 geElement2() {
return element2;
}
public void setElement2(Element2 element2) {
this.element2 = element2;
}
public static class Element2{
protected InnerXlmElement innerXlmElement;
@XmlElement(name = "innerXlmElement")
public InnerXlmElement getInnerXlmElement() {
return innerXlmElement;
}
public void setInnerXlmElement(InnerXlmElement innerXlmElement) {
this.innerXlmElement = innerXlmElement;
}
public static class InnerXlmElement{
protected Item[] item;
@XmlElement(name = "item")
public Item[] getItem() {
return item;
}
public void setItem(Item[] item) {
this.item = item;
}
public static class Item{
protected String itemElement1;
protected String itemElement2;
@XmlElement(name = "itemElement1")
public String getItemElement1() {
return itemElement1;
}
public void setItemElement1(String itemElement1) {
this.svcId = itemElement1;
}
@XmlElement(name = "itemElement2")
public String getItemElement2() {
return itemElement2;
}
public void setItemElement2(String itemElement2) {
this.itemElement2 = itemElement2;
}
}
}
Generated XML, which have been sent to external server during request, looked like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<element1>3324</element1>
<element2>
<innerXlmElement>
<item>
<itemElement1>1</itemElement1>
<itemElement2>2</itemElement2>
</item>
</innerXlmElement>
</element2>
</root>
Of course, generated internal XML wasn't wrapped by CDATA and it hadn't first tag.
So, the question is how wrap XML element by CDATA with Jersey?
Also, I tried solve that problem rudely. I made internal XML by hand and put it to "Req" bean like String:
Req req = new Req();
req.setElement1("some value");
StringBuilder element2= new StringBuilder();
xmlServiceInfo.append("<![CDATA[<?xml version=\"1.0\" encoding=\"UTF-8\" ?><innerXlmElement><item><itemElement1>1</itemElement1><itemElement2>2</itemElement2></item></innerXlmElement>]]>");
req.setElement1(element2.toString());
...
After that manipulation, I have such XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<element1>some value</element1>
<element2>
<![CDATA[<?xml version="1.0" encoding="UTF-8" ?><innerXlmElement><item><itemElement1>1</itemElement1><itemElement1>2</itemElement2></item></innerXlmElement>]]>
</element2>
</root>
Can somebody can tell me, how to avoid converting "<" into "& lt;" and ">" into "& gt;" during Marshalling?
---------- UPDATE (14.01.14) ----------
I have tried "JAXB use String as it is" solution
So, first of all I changed type of element2 to String and added @XmlAnyElement annotation to it in my request bean:
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "root")
public class Req {
protected String element1;
protected String element2;
@XmlElement(name = "element1")
public String getElement1() {
return element1;
}
public void setElement1(String element1) {
this.element1 = element1;
}
@XmlAnyElement(value=CDATAHandler.class)
public String getElement2() {
return element2;
}
public void setElement2(String element2) {
this.element2 = element2;
}
Then I did own implementation of DomHandler as shown here:
public class CDATAHandler implements DomHandler<String, StreamResult> {
private static final String START_TAG = "<![CDATA[<?xml version=\"1.0\" encoding=\"UTF-8\" ?>";
private static final String END_TAG = "]]>";
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(START_TAG) + START_TAG.length();
int endIndex = xml.indexOf(END_TAG);
return xml.substring(beginIndex, endIndex);
}
public Source marshal(String n, ValidationEventHandler errorHandler) {
try {
String xml = START_TAG + n.trim() + END_TAG;
StringReader xmlReader = new StringReader(xml);
return new StreamSource(xmlReader);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
After that changes Jersey became to throw exception (javax.ws.rs.ProcessingException: HTTP 500 Internal Server Error). Here is StackTrace:
org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:226)
org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:655)
org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:652)
org.glassfish.jersey.internal.Errors.process(Errors.java:315)
org.glassfish.jersey.internal.Errors.process(Errors.java:297)
org.glassfish.jersey.internal.Errors.process(Errors.java:228)
org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:422)
org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:652)
org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:412)
action.BaseExecutor.execute(BaseExecutor.java:42) [my method, where I invoke Jersey]
...
I put breakpoints to all methods in my handler, and non of them did not work.
If I remove @XmlAnyElement annotation, all will be fine (of course, except that Jersey will send wrong XML with "& lt;" and "& gt;" escaping).
---------- UPDATE (15.01.14) ----------
I debbuged Jersey and find root exception:
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]