Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize JSON with Jackson into Polymorphic Types - A Complete Example is giving me a compile error

I am attempting to work through a tutorial from Programmer Bruce that is supposed to allow the deserialization of polymorphic JSON.

The complete list can be found here Programmer Bruce tutorials (Great stuff btw)

I have worked through the first five with no problems but I have hit a snag on the last one (Example 6), which of course is the one I really need to get working.

I am getting the following error at compile time

The method readValue(JsonParser, Class) in the type ObjectMapper is not applicable for the arguments (ObjectNode, Class)

and it's being caused by the chunk of code

  public Animal deserialize(         JsonParser jp, DeserializationContext ctxt)          throws IOException, JsonProcessingException     {       ObjectMapper mapper = (ObjectMapper) jp.getCodec();       ObjectNode root = (ObjectNode) mapper.readTree(jp);       Class<? extends Animal> animalClass = null;       Iterator<Entry<String, JsonNode>> elementsIterator =            root.getFields();       while (elementsIterator.hasNext())       {         Entry<String, JsonNode> element=elementsIterator.next();         String name = element.getKey();         if (registry.containsKey(name))         {           animalClass = registry.get(name);           break;         }       }       if (animalClass == null) return null;       return mapper.readValue(root, animalClass);   }   }  

Specifically by the line

return mapper.readValue(root, animalClass);

Has anyone run into this before and if so, was there a solution?

I'd appreciate any help anyone can give Thanks in advance Jon D.

like image 935
Jon Driscoll Avatar asked May 21 '15 00:05

Jon Driscoll


People also ask

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.

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.

How does Jackson deserialize dates from JSON?

How to deserialize Date from JSON using Jackson. In order to correct deserialize a Date field, you need to do two things: 1) Create a custom deserializer by extending StdDeserializer<T> class and override its deserialize(JsonParser jsonparser, DeserializationContext context) method.

What is difference between JsonProperty and JsonAlias?

@JsonProperty can change the visibility of logical property using its access element during serialization and deserialization of JSON. @JsonAlias defines one or more alternative names for a property to be accepted during deserialization.

How to control which fields get serialized/deserialized by Jackson?

How to control which fields get serialized/deserialized by Jackson and which fields get ignored. Control your JSON output with Jackson 2 by using a Custom Serializer. 2. Standard Deserialization Let's start by defining 2 entities and see how Jackson will deserialize a JSON representation to these entities without any customization:

How do I deserialize an object in JSON?

We have type information, and the default JSON (de)serializer can deserialize objects for us. So, instead of manually deserializing every property, we can call into the JsonSerializer.Deserialize()method: return JsonSerializer.Deserialize(ref readerAtStart, targetType, options) as ApiFieldType;

Is there an example of polymorphic deserialization?

Building a custom JsonConverter Kudos to the Microsoft Docs team for providing an example of polymorphic deserialization! This example supports deserializing a type hierarchy of Customer|Employee : Person. However, the example JsonConverteris really built around the shape of those Customerand Employeetypes.

How does JUnit serialization / deserialization work?

A Truck instance is mapped to the named type Truck for example, And It runs a JUnit which checks the serialization / deserialization produces exactly the same object. If we take a look at the produced Json, it looks like the following: Jackson has added a @type attribute to each vehicle json.


1 Answers

As promised, I'm putting an example for how to use annotations to serialize/deserialize polymorphic objects, I based this example in the Animal class from the tutorial you were reading.

First of all your Animal class with the Json Annotations for the subclasses.

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo;  @JsonIgnoreProperties(ignoreUnknown = true) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) @JsonSubTypes({     @JsonSubTypes.Type(value = Dog.class, name = "Dog"),      @JsonSubTypes.Type(value = Cat.class, name = "Cat") } ) public abstract class Animal {      private String name;      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     }  } 

Then your subclasses, Dog and Cat.

public class Dog extends Animal {      private String breed;      public Dog() {      }      public Dog(String name, String breed) {         setName(name);         setBreed(breed);     }      public String getBreed() {         return breed;     }      public void setBreed(String breed) {         this.breed = breed;     } }  public class Cat extends Animal {      public String getFavoriteToy() {         return favoriteToy;     }      public Cat() {}      public Cat(String name, String favoriteToy) {         setName(name);         setFavoriteToy(favoriteToy);     }      public void setFavoriteToy(String favoriteToy) {         this.favoriteToy = favoriteToy;     }      private String favoriteToy;  } 

As you can see, there is nothing special for Cat and Dog, the only one that know about them is the abstract class Animal, so when deserializing, you'll target to Animal and the ObjectMapper will return the actual instance as you can see in the following test:

public class Test {      public static void main(String[] args) {          ObjectMapper objectMapper = new ObjectMapper();          Animal myDog = new Dog("ruffus","english shepherd");          Animal myCat = new Cat("goya", "mice");          try {             String dogJson = objectMapper.writeValueAsString(myDog);              System.out.println(dogJson);              Animal deserializedDog = objectMapper.readValue(dogJson, Animal.class);              System.out.println("Deserialized dogJson Class: " + deserializedDog.getClass().getSimpleName());              String catJson = objectMapper.writeValueAsString(myCat);              Animal deseriliazedCat = objectMapper.readValue(catJson, Animal.class);              System.out.println("Deserialized catJson Class: " + deseriliazedCat.getClass().getSimpleName());            } catch (Exception e) {             e.printStackTrace();         }      } } 

Output after running the Test class:

{"@type":"Dog","name":"ruffus","breed":"english shepherd"}

Deserialized dogJson Class: Dog

{"@type":"Cat","name":"goya","favoriteToy":"mice"}

Deserialized catJson Class: Cat

Hope this helps,

Jose Luis

like image 63
jbarrueta Avatar answered Sep 19 '22 14:09

jbarrueta