JAXB / Jersey - JSONObject as XmlElement
If I got it right, you want to embedd a raw json for free-from values.
@XmlRootElement(name="moo")@XmlAccessorType(XmlAccessType.FIELD)public class Moo { Object json; @JsonRawValue public String getJson() { // default raw value: null or "[]" return json == null ? null : json.toString(); } public void setJson(JsonNode node) { this.json = node; }}
Taken from here.
There are multiple problemsFirst of all this code
import com.fasterxml.jackson.annotation.JsonProperty;import com.fasterxml.jackson.databind.DeserializationFeature;import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.SerializationFeature;import com.fasterxml.jackson.databind.node.ArrayNode;import com.fasterxml.jackson.databind.node.ObjectNode;import com.fasterxml.jackson.dataformat.xml.XmlMapper;import com.sun.xml.bind.marshaller.CharacterEscapeHandler;import org.w3c.dom.ls.LSResourceResolver;import org.xml.sax.*;import javax.xml.bind.JAXBContext;import javax.xml.bind.Marshaller;import javax.xml.bind.Unmarshaller;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlElement;import javax.xml.bind.annotation.XmlRootElement;import javax.xml.bind.annotation.adapters.XmlAdapter;import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;import javax.xml.validation.Schema;import javax.xml.validation.TypeInfoProvider;import javax.xml.validation.Validator;import javax.xml.validation.ValidatorHandler;import java.io.File;import java.io.IOException;import java.io.Writer;public class TestObjectNode { @XmlRootElement(name = "moo") @XmlAccessorType(XmlAccessType.FIELD) static class Moo { @JsonProperty("obj") @XmlElement @XmlJavaTypeAdapter(ObjMarshaller.class) JsonNode obj; } static final ObjectMapper JSON_MAPPER = new ObjectMapper(); static final XmlMapper XML_MAPPER = (XmlMapper) new XmlMapper() .disable(SerializationFeature.WRAP_ROOT_VALUE) .disable(DeserializationFeature.UNWRAP_ROOT_VALUE); public static void main(String[] args) throws Exception { final String value = "{\"obj\": {\"my\": \"custom\", \"object\": 1, \"here\": [1, 2, 3] } }"; Moo moo = JSON_MAPPER.readValue(value, Moo.class); System.out.println(moo.obj); saveToFile(moo); System.out.println(XML_MAPPER.writeValueAsString(moo)); moo = readFromFile(); System.out.println(moo.obj); } private static void saveToFile(Moo moo) throws Exception { File file = new File("moo.xml"); JAXBContext jaxbContext = JAXBContext.newInstance(Moo.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); jaxbMarshaller.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() { @Override public void escape(char[] chars, int i, int i1, boolean b, Writer writer) throws IOException { writer.write(chars, i, i1); } }); jaxbMarshaller.marshal(moo, file); jaxbMarshaller.marshal(moo, System.out); } private static Moo readFromFile() throws Exception { File file = new File("moo.xml"); JAXBContext jaxbContext = JAXBContext.newInstance(Moo.class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); MySchema mySchema = new MySchema(); unmarshaller.setSchema(mySchema); Moo moo = (Moo) unmarshaller.unmarshal(file); String objectNode = mySchema.validatorHandler.getObjectNodeString(); System.out.println("objectNode = " + objectNode); JsonNode jsonNode = XML_MAPPER.readValue(objectNode, ObjectNode.class); moo.obj = jsonNode; return moo; } private static class ObjMarshaller extends XmlAdapter<String, JsonNode> { @Override public JsonNode unmarshal(String v) throws Exception { throw new IllegalArgumentException("i'm never here///"); } @Override public String marshal(JsonNode v) throws Exception { return XML_MAPPER.writeValueAsString(v); } } static class MySchema extends Schema { final ValidatorHandlerImpl validatorHandler = new ValidatorHandlerImpl(); @Override public Validator newValidator() { return null; } @Override public ValidatorHandler newValidatorHandler() { return validatorHandler; } } static class ValidatorHandlerImpl extends ValidatorHandler { private final StringBuilder objectNode = new StringBuilder(); private boolean insideObjectNode = false; public String getObjectNodeString() { return objectNode.toString(); } @Override public void setContentHandler(ContentHandler receiver) { } @Override public ContentHandler getContentHandler() { return null; } @Override public void setErrorHandler(ErrorHandler errorHandler) { } @Override public ErrorHandler getErrorHandler() { return null; } @Override public void setResourceResolver(LSResourceResolver resourceResolver) { } @Override public LSResourceResolver getResourceResolver() { return null; } @Override public TypeInfoProvider getTypeInfoProvider() { return null; } @Override public void setDocumentLocator(Locator locator) { } @Override public void startDocument() throws SAXException { } @Override public void endDocument() throws SAXException { } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { } @Override public void endPrefixMapping(String prefix) throws SAXException { } @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if(localName.equals("ObjectNode")) { insideObjectNode = true; } if (insideObjectNode) { objectNode.append("<" + localName + ">"); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (insideObjectNode) { objectNode.append("</" + localName + ">"); } if(localName.equals("ObjectNode")) { insideObjectNode = false; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (insideObjectNode) { objectNode.append(ch, start, length); } } @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } @Override public void processingInstruction(String target, String data) throws SAXException { } @Override public void skippedEntity(String name) throws SAXException { } }}
Provides following output
{"my":"custom","object":1,"here":[1,2,3]}<?xml version="1.0" encoding="UTF-8" standalone="yes"?><moo> <obj><ObjectNode><my>custom</my><object>1</object><here>1</here><here>2</here><here>3</here></ObjectNode></obj></moo><Moo><obj><my>custom</my><object>1</object><here>1</here><here>2</here><here>3</here></obj></Moo>objectNode = <ObjectNode><my>custom</my><object>1</object><here>1</here><here>2</here><here>3</here></ObjectNode>{"my":"custom","object":"1","here":"3"}
So, this because Jackson XML could not parse "here" as array, unfortunately.So, we should serialize it in different way:
import com.alibaba.fastjson.JSON;import com.fasterxml.jackson.annotation.JsonProperty;import com.fasterxml.jackson.databind.DeserializationFeature;import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;import com.fasterxml.jackson.databind.SerializationFeature;import com.fasterxml.jackson.databind.node.ArrayNode;import com.fasterxml.jackson.databind.node.ObjectNode;import com.fasterxml.jackson.databind.node.TextNode;import com.fasterxml.jackson.dataformat.xml.XmlMapper;import com.sun.xml.bind.marshaller.CharacterEscapeHandler;import org.w3c.dom.ls.LSResourceResolver;import org.xml.sax.*;import javax.xml.bind.JAXBContext;import javax.xml.bind.Marshaller;import javax.xml.bind.Unmarshaller;import javax.xml.bind.annotation.XmlAccessType;import javax.xml.bind.annotation.XmlAccessorType;import javax.xml.bind.annotation.XmlElement;import javax.xml.bind.annotation.XmlRootElement;import javax.xml.bind.annotation.adapters.XmlAdapter;import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;import javax.xml.validation.Schema;import javax.xml.validation.TypeInfoProvider;import javax.xml.validation.Validator;import javax.xml.validation.ValidatorHandler;import java.io.File;import java.io.IOException;import java.io.Writer;public class TestObjectNode { @XmlRootElement(name = "moo") @XmlAccessorType(XmlAccessType.FIELD) static class Moo { @JsonProperty("obj") @XmlElement @XmlJavaTypeAdapter(ObjMarshaller.class) JsonNode obj; } static final ObjectMapper JSON_MAPPER = new ObjectMapper(); static final XmlMapper XML_MAPPER = (XmlMapper) new XmlMapper() .disable(SerializationFeature.WRAP_ROOT_VALUE) .disable(DeserializationFeature.UNWRAP_ROOT_VALUE); public static void main(String[] args) throws Exception { final String value = "{\"obj\": {\"my\": \"custom\", \"object\": 1, \"here\": [1, 2, 3] } }"; Moo moo = JSON_MAPPER.readValue(value, Moo.class); System.out.println(moo.obj); saveToFile(moo); System.out.println(XML_MAPPER.writeValueAsString(moo)); moo = readFromFile(); System.out.println(moo.obj); } private static void saveToFile(Moo moo) throws Exception { File file = new File("moo.xml"); JAXBContext jaxbContext = JAXBContext.newInstance(Moo.class); Marshaller jaxbMarshaller = jaxbContext.createMarshaller(); jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); jaxbMarshaller.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() { @Override public void escape(char[] chars, int i, int i1, boolean b, Writer writer) throws IOException { writer.write(chars, i, i1); } }); jaxbMarshaller.marshal(moo, file); jaxbMarshaller.marshal(moo, System.out); } private static Moo readFromFile() throws Exception { File file = new File("moo.xml"); JAXBContext jaxbContext = JAXBContext.newInstance(Moo.class); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); MySchema mySchema = new MySchema(); unmarshaller.setSchema(mySchema); Moo moo = (Moo) unmarshaller.unmarshal(file); String objectNode = mySchema.validatorHandler.getObjectNodeString(); System.out.println("objectNode = " + objectNode); JsonNode jsonNode = XML_MAPPER.readValue(objectNode, ObjectNode.class); Object hereObject = jsonNode.get("here"); if (hereObject instanceof TextNode) { String hereArray = ((TextNode) hereObject).asText().replace("<![CDATA[", "").replace("]]>", ""); ArrayNode here = JSON_MAPPER.readValue(hereArray, ArrayNode.class); ((ObjectNode) jsonNode).put("here", here); } moo.obj = jsonNode; return moo; } private static class ObjMarshaller extends XmlAdapter<String, JsonNode> { @Override public JsonNode unmarshal(String v) throws Exception { return null; } @Override public String marshal(JsonNode v) throws Exception { Object hereObject = v.get("here"); if (hereObject instanceof ArrayNode) { ArrayNode here = (ArrayNode) hereObject; final StringBuilder value = new StringBuilder("<![CDATA[").append('['); for (int i = 0; i < here.size(); ++i) { if (i > 0) { value.append(','); } value.append(here.get(i)); } value.append(']').append("]]>"); ((ObjectNode) v).put("here", value.toString()); } return XML_MAPPER.writeValueAsString(v); } } static class MySchema extends Schema { final ValidatorHandlerImpl validatorHandler = new ValidatorHandlerImpl(); @Override public Validator newValidator() { return null; } @Override public ValidatorHandler newValidatorHandler() { return validatorHandler; } } static class ValidatorHandlerImpl extends ValidatorHandler { private final StringBuilder objectNode = new StringBuilder(); private boolean insideObjectNode = false; public String getObjectNodeString() { return objectNode.toString(); } @Override public void setContentHandler(ContentHandler receiver) { } @Override public ContentHandler getContentHandler() { return null; } @Override public void setErrorHandler(ErrorHandler errorHandler) { } @Override public ErrorHandler getErrorHandler() { return null; } @Override public void setResourceResolver(LSResourceResolver resourceResolver) { } @Override public LSResourceResolver getResourceResolver() { return null; } @Override public TypeInfoProvider getTypeInfoProvider() { return null; } @Override public void setDocumentLocator(Locator locator) { } @Override public void startDocument() throws SAXException { } @Override public void endDocument() throws SAXException { } @Override public void startPrefixMapping(String prefix, String uri) throws SAXException { } @Override public void endPrefixMapping(String prefix) throws SAXException { } @Override public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { if(localName.equals("ObjectNode")) { insideObjectNode = true; } if (insideObjectNode) { objectNode.append("<" + localName + ">"); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (insideObjectNode) { objectNode.append("</" + localName + ">"); } if(localName.equals("ObjectNode")) { insideObjectNode = false; } } @Override public void characters(char[] ch, int start, int length) throws SAXException { if (insideObjectNode) { objectNode.append(ch, start, length); } } @Override public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } @Override public void processingInstruction(String target, String data) throws SAXException { } @Override public void skippedEntity(String name) throws SAXException { } }}
Results:
{"my":"custom","object":1,"here":[1,2,3]}<?xml version="1.0" encoding="UTF-8" standalone="yes"?><moo> <obj><ObjectNode><my>custom</my><object>1</object><here><![CDATA[[1,2,3]]]></here></ObjectNode></obj></moo><Moo><obj><my>custom</my><object>1</object><here><![CDATA[[1,2,3]]]></here></obj></Moo>objectNode = <ObjectNode><my>custom</my><object>1</object><here><![CDATA[[1,2,3]]]></here></ObjectNode>{"my":"custom","object":"1","here":[1,2,3]}
This is only example, how the exact your task could be achieved. You see that it needed some hard coding, so maybe it's better to think about another approach. Maybe @XmlAnyElement could help you. Or use text variant from previous answer.Also if you delete CharacterEscapeHandler.class you would receive escaped xml string.