2

I need to wrap some arbitrary JSON content into a POJO that is then serialized with MOXy/JAXB to JSON, but could not figure out how to bind a JsonObject with JAXB. I only need to marshall the JsonObject, unmarshalling is not required.

i.e. having the POJO:

@XmlRootElement
public class MsgPOJO {
  public String type;
  public Object content;
}

how to put an arbitrary JSON content in 'MsgPOJO.content', and serialize it:

String jsonDoc = "{\"prop\":\"value\"}";
MsgPOJO msg = new MsgPOJO();
msg.type = "whatever";
msg.content = jsonDoc;

so that this would be the output:

{
  "type": "whatever",
  "content": {
    "prop": "value"
   }
}

I was thinking about annotating the MsgPOJO.content with a @XmlJavaTypeAdapter, but this does not seem to get me anywhere, since the JSON content could be arbitrary.

It would be nice if moxy could marshal JsonObject or JsonStructure, so I could just define the POJO like:

@XmlRootElement
public class MsgPOJO {
  public String type;
  public JsonObject content;
}

Is there a way to make this work? Or is it a limitation in MOXy/JAXB?

Paul Sweatte
  • 24,148
  • 7
  • 127
  • 265
siddhadev
  • 16,501
  • 2
  • 28
  • 35
  • One thing to consider is Gson http://google-gson.googlecode.com/svn-history/trunk/gson/docs/javadocs/com/google/gson/Gson.html Its a library that will turn any class into a Json string and back again. You can use it instead of doing it yourself, it has always worked for me. – REGAL Feb 24 '15 at 18:06

1 Answers1

0

MOXy doesn't support marshal/unmarshal of JSON-P structures by default, you need to implement XmlJavaTypeAdapter. Below is example for JsonObject adapter.

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

If you don't want String, you can write general structure with the help of MyList and MyMap structures. After that, you can write XmlJavaTypeAdapter, which will marshal JsonObject to this new type. The result will be json, not reallly the same as string representation of the input, but legal json.

https://github.com/eclipse/eclipselink.runtime/blob/master/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/rs/model/MyList.java

https://github.com/eclipse/eclipselink.runtime/blob/master/moxy/eclipselink.moxy.test/src/org/eclipse/persistence/testing/jaxb/rs/model/MyMap.java

Martin Vojtek
  • 391
  • 3
  • 7
  • @mvojtek: thank you for your answer, this is similar to what I was trying - the problem ist however it gets marshalled as: `{"msgPOJO":{"type":"myType","content":{"jsonObject":"{\"prop\":\"value\"}"}}}` and not as: `{"msgPOJO":{"type":"myType","content":{"jsonObject":{"prop":"value"}}}}`, i.e. the `XmlAdapter` is just calling `JsonObject.toString()`, so `content.jsonObject` gets serialized as a String and not as an JSON object/map. – siddhadev Feb 25 '15 at 09:54
  • I have updated my answer with some hints about adapter without string result. – Martin Vojtek Feb 25 '15 at 17:00
  • It still won't work if you hava a map of lists or map of maps, i.e. it will work only for simple string to string objects, or string arrays. What I need is a way to pass generic JSON Object in a JAXB/MOXy POJO. – siddhadev Feb 26 '15 at 10:47
  • It should work, if you build general structure for this. Something like GeneralStructure { MyList myList; MyMap myMap; String terminal;} It is not very nice, but should work. – Martin Vojtek Feb 26 '15 at 10:56