Java – eclipse link Moxy JSON serialization
I have an example class:
class Zoo { public Collection<? extends Animal> animals; }
When serializing with Moxy, I get:
{ "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" } ] }
Why use the array indicator "[]", while birds, cats and dogs are not arrays? Second, is there any way to get rid of "birds", "Cats" and "dogs"?
In other words, I try to:
{ { "name": "bird-1","preferredFood": "food-1" },{ "name": "cat-1","favoriteToy": "toy-1" },{ "name": "dog-1","leashColor": "black" } }
Thank you, Behzad
Solution
Question #1
To get this JSON representation, you have mapped the model to the @ xmlelementref annotation, which tells JAXB to use the value of the @ xmlrootelement annotation as the inheritance indicator Using the JSON binding of Moxy becomes the key We make the values of these key JSON values as keys are not allowed to be repeated
zoo
In your model, your animal field / attribute has @ xmlelementref annotation
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 subclass, there is a @ xmlrootelement annotation
import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement public 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" } ] }
Learn more
> http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
Question #2:
You will need some kind of inheritance metric to represent various subclasses
Options #1 – @ xmldescriptornode / @ xmldescriptorvalue
Here I use @ xmldescriptornode / @ xmldescriptorvalue annotation of Moxy
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,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","preferredFood" : "food-1" },{ "type" : "cat","name" : "cat-1","favoriteToy" : "toy-1" },{ "type" : "dog","name" : "dog-1","leashColor" : "black" } ] }
Learn more
> http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-moxy-extension.html
Options #2 – @ xmlclassextractor
ClassExtractor(AnimalExtractor)
You can write some code to determine the appropriate subclass based on 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 classextractor
import javax.xml.bind.annotation.*; import org.eclipse.persistence.oxm.annotations.XmlClassExtractor; @XmlAccessorType(XmlAccessType.FIELD) @XmlSeeAlso({Bird.class,Dog.class}) @XmlClassExtractor(AnimalExtractor.class) public abstract class Animal { private String name; }
bird
Because of how Moxy handles @ xmlelement and @ xmlattribute annotations, any data you want to make available to classextractor needs 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" } ] }
Learn more
> http://blog.bdoughan.com/2012/02/jaxb-and-inheritance-eclipselink-moxy.html
Demo code
The following demonstration code can be used for the above two mappings
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); } }