EclipseLink MOXy JSON Serialization EclipseLink MOXy JSON Serialization json json

EclipseLink MOXy JSON Serialization


QUESTION #1

Why is it using array indicators "[]", while bird, cat, and dog are not arrays?

To get this JSON representation you have mapped your model with the @XmlElementRef annotation which tells JAXB to use the value of the @XmlRootElement annotation as the inheritance indicator. With MOXy's JSON binding these become keys. We make the value of these keys JSON arrays since keys are not allowed to repeat.

Zoo

In your model you have the @XmlElementRef annotation on your animals field/property.

import java.util.Collection;import javax.xml.bind.annotation.XmlElementRef;class Zoo {    @XmlElementRef    public Collection<? extends Animal> animals;}

Animal

import javax.xml.bind.annotation.*;@XmlAccessorType(XmlAccessType.FIELD)@XmlSeeAlso({Bird.class, Cat.class, Dog.class})public abstract class Animal {    private String name;}

Bird

On each of your subclasses you have an @XmlRootElement annotation.

import javax.xml.bind.annotation.XmlRootElement;@XmlRootElementpublic class Bird extends Animal {    private String wingSpan;    private String preferredFood;}

input.json/Output

{   "bird" : [ {      "name" : "bird-1",      "wingSpan" : "6 feets",      "preferredFood" : "food-1"   } ],   "cat" : [ {      "name" : "cat-1",      "favoriteToy" : "toy-1"   } ],   "dog" : [ {      "name" : "dog-1",      "breed" : "bread-1",      "leashColor" : "black"   } ]}

For More Information


QUESTION #2

Second, is there a way to get rid of "bird", "cat", and "dog"?

You are going to need some sort of inheritance indicator to represent the various subclasses.

OPTION #1 - @XmlDescriminatorNode/@XmlDescriminatorValue

Here I do this using MOXy's @XmlDescriminatorNode/@XmlDescriminatorValue annotations.

Zoo

import java.util.Collection;class Zoo {    public Collection<? extends Animal> animals;}

Animal

import javax.xml.bind.annotation.*;import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;@XmlAccessorType(XmlAccessType.FIELD)@XmlSeeAlso({Bird.class, Cat.class, Dog.class})@XmlDiscriminatorNode("@type")public abstract class Animal {    private String name;}

Bird

import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;@XmlDiscriminatorValue("bird")public class Bird extends Animal {    private String wingSpan;    private String preferredFood;}

input.json/Output

{   "animals" : [ {      "type" : "bird",      "name" : "bird-1",      "wingSpan" : "6 feets",      "preferredFood" : "food-1"   }, {      "type" : "cat",      "name" : "cat-1",      "favoriteToy" : "toy-1"   }, {      "type" : "dog",      "name" : "dog-1",      "breed" : "bread-1",      "leashColor" : "black"   } ]}

For More Information

OPTION #2 - @XmlClassExtractor

ClassExtractor (AnimalExtractor)

You can write some code that will determine the appropriate subclass based on the JSON content.

import org.eclipse.persistence.descriptors.ClassExtractor;import org.eclipse.persistence.sessions.*;public class AnimalExtractor extends ClassExtractor {    @Override    public Class extractClassFromRow(Record record, Session session) {        if(null != record.get("@wingSpan") || null != record.get("@preferredFood")) {            return Bird.class;        } else if(null != record.get("@favoriteToy")) {            return Cat.class;        } else {            return Dog.class;        }    }}

Animal

The @XmlClassExtractor annotation is used to specify the ClassExtractor.

import javax.xml.bind.annotation.*;import org.eclipse.persistence.oxm.annotations.XmlClassExtractor;@XmlAccessorType(XmlAccessType.FIELD)@XmlSeeAlso({Bird.class, Cat.class, Dog.class})@XmlClassExtractor(AnimalExtractor.class)public abstract class Animal {    private String name;}

Bird

Due to how MOXy processes the @XmlElement and @XmlAttribute annotations, any of the data you want to be made available to the ClassExtractor will need to be annotated with @XmlAttribute.

import javax.xml.bind.annotation.XmlAttribute;public class Bird extends Animal {    @XmlAttribute    private String wingSpan;    @XmlAttribute    private String preferredFood;}

input.json/Output

{   "animals" : [ {      "wingSpan" : "6 feets",      "preferredFood" : "food-1",      "name" : "bird-1"   }, {      "favoriteToy" : "toy-1",      "name" : "cat-1"   }, {      "breed" : "bread-1",      "leashColor" : "black",      "name" : "dog-1"   } ]}

For More Information


DEMO CODE

The following demo code can be used with both of the mappings described above.

import java.util.*;import javax.xml.bind.*;import javax.xml.transform.stream.StreamSource;import org.eclipse.persistence.jaxb.JAXBContextProperties;public class Demo {    public static void main(String[] args) throws Exception {        Map<String, Object> properties = new HashMap<String, Object>();        properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");        properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);        JAXBContext jc = JAXBContext.newInstance(new Class[] {Zoo.class}, properties);        Unmarshaller unmarshaller = jc.createUnmarshaller();        StreamSource json = new StreamSource("src/forum14210676/input.json");        Zoo zoo = unmarshaller.unmarshal(json, Zoo.class).getValue();        Marshaller marshaller = jc.createMarshaller();        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);        marshaller.marshal(zoo, System.out);    }}