Skip to content

Latest commit

 

History

History

example-06-json-with-jackson

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

SE325 Example 06 - JSON with Jackson

This project contains several examples showing how we can use Jackson to handle marshalling and unmarshalling Java objects to and from JSON.

  • Example 01: A basic example. We can see in Example01Main how the ObjectMapper class provided to us by Jackson, can be used to read and write Java objects as JSON. We can also note that the Book class being read / written has nothing special about it - it's just a plain old Java object (POJO) with no annotations, or inheritance. Basic classes like this will be handled automatically. We can also see that the Genre enum is handled perfectly with no additional configuration.

  • Example 02: Here we can see a (somewhat contrived) example showing how the @JsonGetter and @JsonSetter annotations can be used to override the default JSON property names that would be generated by Jackson. We can also see the @JsonIgnore can be used to ignore a particular property (i.e. it won't be written out to JSON).

  • Example 03: In this example, our Pokemon class has a list of types. With no additional configuration, Jackson will marshall and unmarshall this correctly. However, as shown by Example03Main lines 15 and 24, the deserialized list type will always be ArrayList, even though it was originally created as a LinkedList. Do keep this in mind.

  • Example 04: In this example, we can see that Maps are also able to be marshalled / unmarshalled with no additional configuration, assuming the map's key type is String. However, also note that, as shown by Example04Main lines 15 and 24, the deserialized map type will always be LinkedHashMap, no matter the type of the original map.

  • Example 05: In this example, we can see how we can provide custom serialization and deserialization of certain types, which may not be naturally supported by Jackson. We do this by subclassing StdSerializer and StdDeserializer, and then referring to our implementations using the @JsonSerialize and @JsonDeserialize annotations. In this example, Movie has a release date, which is of type LocalDate. This is not naturally supported by Jackson, so we have written LocalDateSerializer and LocalDateDeserializer classes to handle this.

    In Example 05, we also see that we have a University class, which keeps track of enrollments by mapping Course instances to the Students taking those courses. Jackson doesn't naturally support Maps with non-String key types. To solve this problem, we have provided a CourseDeserializer class, which is a subclass of KeyDeserializer. We then tell Jackson to use this class with another @JsonDeserialize annotation, this with the keyUsing property set.

  • Example 06: In this example, we can see how we might overcome issues caused by cyclic references. In this example, each Employee instance holds a reference to their Manager. Managers in turn hold references to all of their employees. With no additional configuration, this would result in an infinite loop whenever we tried to serialize one of these instances. To get around this, we have used the @JsonIdentifyInfo annotation on the Employee class, specifying that each employee can be uniquely identified by its id property. Whenever an employee is first serialized, the entire employee will be written out. If an employee with the same id is then serialized later on, only its id value will be written out instead.

    Using this method helps overcome the cyclic reference problem, but requires that the unmarshaller on the receiving end knows how to unmarshall JSON which has been written out this way (for example, another app using Jackson). This removes some benefits of using an otherwise platform-independent data interchange format such as JSON. When designing your data objects intended for conversion to / from JSON, consider whether any cyclic references are necessary in the first place.

  • Example 07: In this example, we can see how we can deal with inheritance using Jackson. Zoos have a collection of Animals, which may be either Cats or Dogs. With no configuration, Jackson will be able to serialize a Zoo correctly, but will be unable to deserialize one - it will try to create Animal objects rather than Cat and Dog objects. This is not allowed as Animal is abstract (and even if it were allowed, we would lose any cat- and dog-specific information).

    We can handle this by using the @JsonTypeInfo annotation on Animal, which will allow Jackson to add extra info when serializing, that it can use when deserializing to determine the object type. the use property lets us specify either NAME or CLASS. If we specify NAME, then we additionally need to supply a @JsonSubTypes annotation mapping classes to names. If we use CLASS, then Jackson will simply use the fully qualified (i.e. including package) Java class name. The property property lets us specify the name of the extra JSON property into which Jackson will write the type info.