Как написать XmlAdaptor JsonStructure (JSR-353) для JAXB MOXy?
Мне нужно обернуть произвольный контент JSON в POJO, который затем сериализуется с MOXy/JAXB в JSON, но не смог понять, как связать JsonObject
с JAXB. Мне нужно только маршаллировать JsonObject
Распаковка не требуется.
то есть имея POJO:
@XmlRootElement
public class MsgPOJO {
public String type;
public Object content;
}
как поместить произвольный контент JSON в MsgPOJO.content и сериализовать его:
String jsonDoc = "{\"prop\":\"value\"}";
MsgPOJO msg = new MsgPOJO();
msg.type = "whatever";
msg.content = jsonDoc;
так что это будет вывод:
{
"type": "whatever",
"content": {
"prop": "value"
}
}
Я думал об аннотировании MsgPOJO.content
с @XmlJavaTypeAdapter
, но, похоже, это никуда не приведет, поскольку содержимое JSON может быть произвольным.
Было бы неплохо, если бы Мокси мог встать JsonObject
или же JsonStructure
так что я мог бы просто определить POJO как:
@XmlRootElement
public class MsgPOJO {
public String type;
public JsonObject content;
}
Есть ли способ сделать эту работу? Или это ограничение в MOXy/JAXB?
1 ответ
MOXy по умолчанию не поддерживает маршал / демаршал структур JSON-P, вам нужно реализовать XmlJavaTypeAdapter. Ниже приведен пример для адаптера JsonObject.
MsgPOJO.java
package org.eclipse.persistence.testing.jsonp;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Created by mvojtek on 24/02/15.
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class MsgPOJO {
public String type;
public JsonObjectWrapper content;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public JsonObjectWrapper getContent() {
return content;
}
public void setContent(JsonObjectWrapper content) {
this.content = content;
}
@Override
public String toString() {
return "MsgPOJO{" +
"type='" + type + '\'' +
", content=" + content +
'}';
}
}
JsonObjectWrapper.java
package org.eclipse.persistence.testing.jsonp;
import javax.json.JsonObject;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
* Created by mvojtek on 24/02/15.
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class JsonObjectWrapper {
@XmlJavaTypeAdapter(JsonObjectAdapter.class)
private JsonObject jsonObject;
public JsonObject getJsonObject() {
return jsonObject;
}
public void setJsonObject(JsonObject jsonObject) {
this.jsonObject = jsonObject;
}
@Override
public String toString() {
return "JsonObjectWrapper{" +
"jsonObject=" + jsonObject +
'}';
}
}
JsonObjectAdapter.java
package org.eclipse.persistence.testing.jsonp;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonReader;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import java.io.StringReader;
/**
* Created by mvojtek on 24/02/15.
*/
public final class JsonObjectAdapter extends XmlAdapter<String,JsonObject> {
@Override
public String marshal(JsonObject v) throws Exception {
if (null == v) {
return null;
}
return v.toString();
}
@Override
public JsonObject unmarshal(String v) throws Exception {
if (null == v) {
return null;
}
JsonReader jsonReader = Json.createReader(new StringReader(v));
return jsonReader.readObject();
}
}
Test.java
package org.eclipse.persistence.testing.jsonp;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.oxm.MediaType;
import javax.json.Json;
import javax.json.JsonReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
public class Test {
public static void main(String[] args) throws Exception {
//marshal
JAXBContext jaxbContext = JAXBContextFactory.createContext(new Class[]{MsgPOJO.class}, null);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(JAXBContextProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
MsgPOJO msgPOJO = new MsgPOJO();
msgPOJO.setType("myType");
JsonReader jsonReader = Json.createReader(new StringReader("{\"prop\":\"value\"}"));
JsonObjectWrapper wrapper = new JsonObjectWrapper();
wrapper.setJsonObject(jsonReader.readObject());
msgPOJO.setContent(wrapper);
StringWriter marshallerOutput = new StringWriter();
marshaller.marshal(msgPOJO, marshallerOutput);
String result = marshallerOutput.toString();
System.out.println("marshal result = "+result);
//unmarshal
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.setProperty(JAXBContextProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
unmarshaller.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, true);
MsgPOJO msgPOJO2 = (MsgPOJO)unmarshaller.unmarshal(new StringReader(result));
System.out.println("msgPOJO2="+msgPOJO2);
}
}
Если вы не хотите String, вы можете написать общую структуру с помощью структур MyList и MyMap. После этого вы можете написать XmlJavaTypeAdapter, который будет маршалировать JsonObject для этого нового типа. Результатом будет json, не то же самое, что строковое представление ввода, но допустимый json.