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.
To get started with JSON we suggest including the jackson library through Maven. Please open the Project Structure window and select library:
Search for the “jackson databind” library and select the latest version:
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.
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:
Why does this happen?
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.
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.
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 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.
Please ensure that you are able to store at least lists of objects. Feel free to use any data structure that you find interesting.