2

I'm looking for a Java JSPON serializer that can handle references according to the JSPON specification.

Are there any available that can do this currently? Or is there any way to modify an existing serializer to handle object refs with the $ref notation?

erickson
  • 265,237
  • 58
  • 395
  • 493
ant-depalma
  • 2,006
  • 4
  • 26
  • 34

2 Answers2

0

I would use one of the many Object to JSon serialization libraries. Many of the libraries are extensible, but I suspect adding references could get complicated unless you make some pragmatic choices as to when to use these.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
0

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.

If you are interested in an object-JSON binding approach, below is how it could be done using MOXy. The example below is based on the example one from the JSPON Core Spec:

Parent

The Parent class is the domain object that corresponds to the root of the JSON message. It has two fields that are of type Child.

package forum9862100;

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Parent {

    protected Child field1;
    protected Child field2;

}

Child

The Child class may be referenced by its key. We will handle this use case with an XmlAdapter. We link to an XmlAdapter via the @XmlJavaTypeAdapter annotation.

package forum9862100;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlJavaTypeAdapter(ChildAdapter.class)
@XmlAccessorType(XmlAccessType.FIELD)
public class Child {

    protected String id;
    protected String foo;
    protected Integer bar;

}

ChildAdapter

Below is the implementation of the XmlAdapter. This XmlAdapter is stateful which means we will need to set an instance on the Marshaller and Unmarshaller.

package forum9862100;

import java.util.*;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class ChildAdapter extends XmlAdapter<ChildAdapter.AdaptedChild, Child>{

    private List<Child> childList = new ArrayList<Child>();
    private Map<String, Child> childMap = new HashMap<String, Child>();

    public static class AdaptedChild extends Child {
        @XmlElement(name="$ref")
        public String reference;
    }

    @Override
    public AdaptedChild marshal(Child child) throws Exception {
        AdaptedChild adaptedChild = new AdaptedChild();
        if(childList.contains(child)) {
            adaptedChild.reference = child.id;
        } else {
            adaptedChild.id = child.id;
            adaptedChild.foo = child.foo;
            adaptedChild.bar = child.bar;
            childList.add(child);
        }
        return adaptedChild;
    }

    @Override
    public Child unmarshal(AdaptedChild adaptedChild) throws Exception {
        Child child = childMap.get(adaptedChild.reference);
        if(null == child) {
            child = new Child();
            child.id = adaptedChild.id;
            child.foo = adaptedChild.foo;
            child.bar = adaptedChild.bar;
            childMap.put(child.id, child);
        }
        return child;
    }

}

Demo

The code below demonstrates how to specify a stateful XmlAdapter on the Marshaller and Unmarshaller:

package forum9862100;

import java.io.File;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Parent.class);

        StreamSource json = new StreamSource(new File("src/forum9862100/input.json"));
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty("eclipselink.media-type", "application/json");
        unmarshaller.setProperty("eclipselink.json.include-root", false);
        unmarshaller.setAdapter(new ChildAdapter());
        Parent parent = (Parent) unmarshaller.unmarshal(json, Parent.class).getValue();

        System.out.println(parent.field1 == parent.field2);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty("eclipselink.media-type", "application/json");
        marshaller.setProperty("eclipselink.json.include-root", false);
        marshaller.setAdapter(new ChildAdapter());
        marshaller.marshal(parent, System.out);
    }

}

Output

Below is the output from running the demo code. Note how the two instances of Child pass the identity test.

true
{
   "field1" : {
      "id" : "2",
      "foo" : "val",
      "bar" : 4
   },
   "field2" : {
      "$ref" : "2"
   }}

For More Information

Pyves
  • 6,333
  • 7
  • 41
  • 59
bdoughan
  • 147,609
  • 23
  • 300
  • 400