Het gebruik van Serializable
heeft een paar nadelen. De belangrijkste is dat alleen Java programma’s deze stuctuur kunnen genereren en lezen.
Een meer flexibele manier om jouw data op te slaan is gebruik maken van het JavaScript Object Notatie (JSON) formaat:
{
"name":"John",
"age":30,
"car":null
}
Dit representeert een klasse met drie velden.
Een bijkomend voordeel is dat JSON bestanden makkelijk met de hand geschreven of aangepast kunnen worden.
Om van start te gaan met JSON stellen we voor om jackson bibliotheek te installeren via Maven. Open alsjeblieft het Project Structure scherm en kies library:
Zoek naar de “jackson databind” bibliotheek en selecteer de laatste versie:
Natuurlijk hebben we wat informatie nodig om op te slaan. Dus gebruiken we even de bekende “Person”, Student, Teacher” structuur:
// 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: Het gegeven voorbeeld is een beetje te simpel.
Natuurlijk kan JSON ook hele lijsten met data opslaan, ongeveer zo::
[{"firstName":"Frederik"},{"firstName":"Michael"},{"firstName":"Tristan"}]
Wanneer je datastructuur geneste data bevat, objecten in objecten, dan worden alle member variabelen opgeeslagen inclusief hun interne data.
We raden sterk aan om simpel weg dezelfde code om te draaien om je data weer in te laden. One might think that the following should work:
ObjectMapper mapper = new ObjectMapper();
Teacher loaded = mapper.readValue(new FileInputStream("resources/objects.json"), Teacher.class);
Helaas zegt de formidabele foutmelding die volgt ongeveer het volgende:
Waarom gebeurt dit?
De Jackson bibliotheek kan iedere data structuur opslaan, maar de bibliotheek weet totaal niet hoe jouw objecten en velden er uit zien.
Dus om van start te kunnen gaan geven we de object mappeen een hint. Dat is waarom de .readValue()
methode verteld moet worden dat we een instantie van de Teacher.class
willen lezen uit de file.
Helaas is dat niet genoeg.
Warning: Als deze uitleg te ver gaat voel je vrij om dit deel over te slaan. De manier waarop Jacksom jouw data hersteld maakt gebruik van magie.
Java biedt de mogelijkheid om de structuur te analyseren van jouw classes, en hun member variabelen en zelfs methoden en hun definitie.
Deze mogelijkheid wordt door Jackson gebruikt om een klasse te herbouwen. Zo’n klasse moet dan wel een “default cosntructor” aanbieden. (Een constructor zonder argumenten.) Vervolgens wordt via de setters ieder veld hersteld.
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) { /* ... */ }
}
Helaas ben je ondertussen een best goede programmeur die zijn klassen op een andere manier definieert.
Om JSON jouw klassen te laten aanmaken door gebruik te maken van de constructor zoals jij die ontworp hebt kun je extra informatie aanbieden via de Jackson bibliotheek. We doen dit door gebruik te maken van annotaties:
@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;
}
De @JsonCreator
annotatie identificeert de constructor die gebruikt moet worden.
Daarnaast wordt ieder argument gekoppeld aan een corresponderend JSON veld:
@JsonProperty("firstName") String firstName
Dit geeft je misschien het idee dat de json velden mogelijk een andere naam kunnen hebben dan de argumenten. Je hebt gelijk.
Een enkel object laden is niet heel erg practisch en in het ideale geval wil je een lijst van objecten uit een JSON file kunnen lezen.
Voor lijsten is de volgende formule nodig:
TypeReference<List<Teacher>> typeReference = new TypeReference<>() {};
List<Teacher> myList = objectMapper.readValue(jsonString, typeReference);
Door de type reference vertel je de object mapper welke elementen in de lijst zijn opgeslagen.
Daarnaast is het mogelijk om met annotaties aan te geven of een attribuut een lijst van objecten op slaat.
Zorg ervoor dat je begrijpt hoe je met JSON een lijst van objecten kunt laden. Voel je vrij om een willekeurige data structuur te gebruiken die je interessant vindt.