1.4-Software-Development-Principles

Serialisation with JSON

Introduction

Using Java Serializable has some disadvantages. Primarily only Java programs can generate and read this structure automatically. A more flexible way to store your data is using the JavaScript Object Notation (JSON) format:

{
  "name":"John", 
  "age":30, 
  "car":null
}

This would represent a class that contains three fields.

A very useful additional property is that JSON files can quite easily be written or edited by hand.

Step 1: Register the Jackson library

To get started with JSON we suggest including the jackson library through Maven. Please open the Project Structure window and select library:

Maven

Search for the “jackson databind” library and select the latest version:

Jackson

Step 2: Design (and store) your data model

Obviously we need some information to store. Let’s assume the well known “Person, Student, Teacher” structure for now:

// Create your data structure
Teacher teacher = new Teacher("Frederik", "F.", "Bonte", "fbo11", "ing.");
// JSON requires the ObjectMapper 
ObjectMapper mapper = new ObjectMapper();
// Store the data in a file:
mapper.writeValue(new FileOutputStream("resources/objects.json"), teacher);

This generates a file with the following content:

{"firstName":"Frederik","initials":"F.","lastName":"Bonte","code":"fbo11","title":"ing."}

Note: The example given here is really quite simple.

Clearly JSON allows the export of an entire ArrayList with data, somewhat like this:

[{"firstName":"Frederik"},{"firstName":"Michael"},{"firstName":"Tristan"}]

If your data structure contains nested data, objects within objects, all the member variables and their internal data are stored as well.

Step 3: Restoring your data

We strongly suggest that you simply try to run the reverse instruction to reload your data. One might think that the following should work:

ObjectMapper mapper = new ObjectMapper();
Teacher loaded = mapper.readValue(new FileInputStream("resources/objects.json"), Teacher.class);

Regrettably the tremendous exception that follows says something like this:

JSON Exception

Why does this happen?

Data reload

The Jackson library is able to store any data structure, but this library has no idea which objects you defined and their actual fields.

So in order to get started you need to give the object mapper a hint That is why the .readValue() method need to be told that you’re trying to read an instance of the Teacher.class from the file.

Regrettably that is clearly not enough.

Reflection

Warning: If this goes a bit deep for you. Feel free to skip this section. Restoring data using Jackson is done through magic.

Java provides the ability to analyse the structure of your classes, their member variables, their methods and their signature, etc.

This feature can be used by Jackson to restore a basic class. Such a class must have a “default constructor” (One without any arguments), and setters for each field that is stored:

public class Teacher {
    private String title;
    private String code;

    public String getTitle() { /* ... */ }
    public void setTitle(String title) { /* ... */ }
    
    public String getCode() { /* ... */ }
    public void setCode(String code) { /* ... */ }
}

Regrettably you are a rather good programmer and designed you classes differently.

JSON Annotations

In order to make JSON read your classes and call the constructor in the way you designed them, you need to give the Jackson library some additional information. You can do this through the use of annotations:

@JsonCreator
public Teacher(
        @JsonProperty("firstName") String firstName,
        @JsonProperty("initials") String initials,
        @JsonProperty("lastName") String lastName,
        @JsonProperty("code") String code,
        @JsonProperty("title") String title) {
    super(firstName, initials, lastName);
    this.code = code;
    this.title = title;
}

The @JsonCreator annotation identifies the constructor to use. Additionally each argument is mapped to a corresponding json property:

@JsonProperty("firstName") String firstName

This might give you the idea that the json properties may be represented with a different name than the name you give your arguments. You are correct.

Loading a list of objects

Loading a single object is not very practical and ideally you want to be able to load a list of object from a JSON file.

For loading a list of object the following can be used:

TypeReference<List<Teacher>> typeReference = new TypeReference<>() {};
List<Teacher> myList = objectMapper.readValue(jsonString, typeReference);

By creating a type reference you tell the object mapper what kind of objects are stored in the list.

Also by using annotations you can tell Jackson if a certain attribute is a list of objects as well.

The way forward

Please ensure that you are able to store at least lists of objects. Feel free to use any data structure that you find interesting.