Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I polymorphic deserialization Json String using Java and Jackson Library?

I've some classes A, B, C they all inherit from class BaseClass.

I've a String json that contains the json representation of the A, B, C or BaseClass.

I want to have some way to deserialize this String to the BaseClass (polymorphic deserialization). Something like this

BaseClass base = ObjectMapper.readValue(jsonString, BaseClass.class); 

jsonString could be Json String representation of any of A, B, C, or BaseClass.

like image 661
mohamede1945 Avatar asked Jul 01 '11 01:07

mohamede1945


People also ask

What is polymorphic deserialization?

A polymorphic deserialization allows a JSON payload to be deserialized into one of the known gadget classes that are documented in SubTypeValidator. java in jackson-databind in GitHub. The deserialized object is assigned to a generic base class in your object model, such as java. lang.

What is polymorphic JSON?

You can use this schema when defining XML Type hierarchies by using only the base XML Types. The XML schema defines XML Types that inherit from each other. In the JSON, an object carries no additional information about the type.

What is deserialization of JSON in Java?

Deserialization is transforming the data from a file or stream back into an object to be used in your application. This can be binary data or structured data like JSON and XML. Deserialization is the opposite of serialization, which transforms objects into byte streams or structured text.

How does Jackson parse JSON?

databind. ObjectMapper ) is the simplest way to parse JSON with Jackson. The Jackson ObjectMapper can parse JSON from a string, stream or file, and create a Java object or object graph representing the parsed JSON. Parsing JSON into Java objects is also referred to as to deserialize Java objects from JSON.


1 Answers

It's not clear what problem the original poster is having. I'm guessing that it's one of two things:

  1. Deserialization problems with unbound JSON elements, because the JSON contains elements for which there is nothing in the Java to bind to; or

  2. Want to implement polymorphic deserialization.

Here's a solution to the first problem.

import static org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES;  import org.codehaus.jackson.map.ObjectMapper;  public class Foo {   public static void main(String[] args) throws Exception   {     BaseClass base = new BaseClass();     A a = new A();     B b = new B();     C c = new C();      ObjectMapper mapper = new ObjectMapper();      String baseJson = mapper.writeValueAsString(base);     System.out.println(baseJson); // {"baseName":"base name"}     String aJson = mapper.writeValueAsString(a);     System.out.println(aJson); // {"baseName":"base name","aName":"a name"}     String bJson = mapper.writeValueAsString(b);     System.out.println(bJson); // {"baseName":"base name","bName":"b name"}     String cJson = mapper.writeValueAsString(c);     System.out.println(cJson); // {"baseName":"base name","cName":"c name"}      BaseClass baseCopy = mapper.readValue(baseJson, BaseClass.class);     System.out.println(baseCopy); // baseName: base name      // BaseClass aCopy = mapper.readValue(aJson, BaseClass.class);     // throws UnrecognizedPropertyException:      // Unrecognized field "aName", not marked as ignorable     // because the JSON contains elements for which no Java field     // to bind to was provided.      // Need to let Jackson know that not all JSON elements must be bound.     // To resolve this, the class can be annotated with      // @JsonIgnoreProperties(ignoreUnknown=true) or the ObjectMapper can be     // directly configured to not FAIL_ON_UNKNOWN_PROPERTIES     mapper = new ObjectMapper();     mapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);      BaseClass aCopy = mapper.readValue(aJson, BaseClass.class);     System.out.println(aCopy); // baseName: base name     BaseClass bCopy = mapper.readValue(bJson, BaseClass.class);     System.out.println(bCopy); // baseName: base name     BaseClass cCopy = mapper.readValue(cJson, BaseClass.class);     System.out.println(cCopy); // baseName: base name   } }  class BaseClass {   public String baseName = "base name";   @Override public String toString() {return "baseName: " + baseName;} }  class A extends BaseClass {   public String aName = "a name";   @Override public String toString() {return super.toString() + ", aName: " + aName;} }  class B extends BaseClass {   public String bName = "b name";   @Override public String toString() {return super.toString() + ", bName: " + bName;} }  class C extends BaseClass {   public String cName = "c name";   @Override public String toString() {return super.toString() + ", cName: " + cName;} } 

Here's a solution to the second problem.

import org.codehaus.jackson.annotate.JsonSubTypes; import org.codehaus.jackson.annotate.JsonSubTypes.Type; import org.codehaus.jackson.annotate.JsonTypeInfo; import org.codehaus.jackson.map.ObjectMapper;  public class Foo {   public static void main(String[] args) throws Exception   {     BaseClass base = new BaseClass();     A a = new A();     B b = new B();     C c = new C();      ObjectMapper mapper = new ObjectMapper();      String baseJson = mapper.writeValueAsString(base);     System.out.println(baseJson); // {"type":"BaseClass","baseName":"base name"}     String aJson = mapper.writeValueAsString(a);     System.out.println(aJson); // {"type":"a","baseName":"base name","aName":"a name"}     String bJson = mapper.writeValueAsString(b);     System.out.println(bJson); // {"type":"b","baseName":"base name","bName":"b name"}     String cJson = mapper.writeValueAsString(c);     System.out.println(cJson); // {"type":"c","baseName":"base name","cName":"c name"}      BaseClass baseCopy = mapper.readValue(baseJson, BaseClass.class);     System.out.println(baseCopy); // baseName: base name     BaseClass aCopy = mapper.readValue(aJson, BaseClass.class);     System.out.println(aCopy); // baseName: base name, aName: a name     BaseClass bCopy = mapper.readValue(bJson, BaseClass.class);     System.out.println(bCopy); // baseName: base name, bName: b name     BaseClass cCopy = mapper.readValue(cJson, BaseClass.class);     System.out.println(cCopy); // baseName: base name, cName: c name   } }  @JsonTypeInfo(       use = JsonTypeInfo.Id.NAME,       include = JsonTypeInfo.As.PROPERTY,       property = "type")   @JsonSubTypes({       @Type(value = A.class, name = "a"),       @Type(value = B.class, name = "b"),       @Type(value = C.class, name = "c") })  class BaseClass {   public String baseName = "base name";   @Override public String toString() {return "baseName: " + baseName;} }  class A extends BaseClass {   public String aName = "a name";   @Override public String toString() {return super.toString() + ", aName: " + aName;} }  class B extends BaseClass {   public String bName = "b name";   @Override public String toString() {return super.toString() + ", bName: " + bName;} }  class C extends BaseClass {   public String cName = "c name";   @Override public String toString() {return super.toString() + ", cName: " + cName;} } 

If instead, the goal is to deserialize to a subclass type without a JSON element specifically dedicated to indicate what the subclass type is, then that is also possible, so long as something in the JSON can be used to decide what the subclass type should be. I posted an example of this approach at http://programmerbruce.blogspot.com/2011/05/deserialize-json-with-jackson-into.html.

like image 92
Programmer Bruce Avatar answered Oct 25 '22 22:10

Programmer Bruce