1. Lecture du JSON dans Map et List
En général, lorsque nous travaillons avec du JSON, nous connaissons à l’avance sa structure et pouvons la décrire sous forme de classe Java. Mais en pratique, tout n’est pas aussi prévisible : des champs peuvent apparaître et disparaître, l’imbrication peut changer. Dans ces cas, il est souvent plus pratique de manipuler des structures de données universelles — Map, List — ou des arbres JSON.
Créer un modèle Java pour chaque variation est long et fragile. Les collections universelles et les arbres permettent d’extraire de manière flexible uniquement les parties nécessaires, sans s’attacher à un schéma figé.
Désérialisation d’un objet JSON dans Map
Exemple de JSON :
{
"id": 123,
"name": "Alice",
"email": "alice@example.com",
"active": true
}
Désérialisation dans Map :
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Map;
public class Main {
public static void main(String[] args) throws Exception {
String json = "{\"id\":123,\"name\":\"Alice\",\"email\":\"alice@example.com\",\"active\":true}";
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> data = mapper.readValue(json, Map.class);
System.out.println(data);
// Sortie : {id=123, name=Alice, email=alice@example.com, active=true}
}
}
Vous pouvez maintenant accéder à n’importe quel champ comme à un élément de map :
System.out.println(data.get("name")); // Alice
Désérialisation d’un tableau d’objets dans List
Tableau JSON :
[
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" }
]
Désérialisation :
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.Map;
public class UsersReadExample {
public static void main(String[] args) throws Exception {
String json = "[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]";
ObjectMapper mapper = new ObjectMapper();
List<Map<String, Object>> users = mapper.readValue(json, List.class);
for (Map<String, Object> user : users) {
System.out.println(user.get("name"));
}
// Sortie :
// Alice
// Bob
}
}
Point important : lors de la lecture dans des structures universelles, tous les objets imbriqués deviennent des Map, et les tableaux — des List. Pour des structures complexes, il faudra convertir les types avec précaution et les vérifier :
Object items = data.get("items");
if (items instanceof List) {
List<?> itemList = (List<?>) items;
// ...
}
2. JsonNode : arbre JSON dans Jackson
Travailler avec Map/List est pratique mais peu sûr : on peut se tromper de type ou manquer une imbrication. Jackson propose un outil plus puissant — la classe JsonNode. C’est un arbre universel où chaque nœud est un objet, un tableau, une valeur ou null.
Obtention d’un JsonNode
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
public class JsonNodeStart {
public static void main(String[] args) throws Exception {
String json = "{\"id\":123,\"name\":\"Alice\",\"tags\":[\"java\",\"json\"]}";
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
// root — c’est la racine de l’arbre JSON
}
}
Accès aux champs
int id = root.get("id").asInt(); // 123
String name = root.get("name").asText(); // Alice
JsonNode tags = root.get("tags"); // tableau
System.out.println("Nom: " + name);
Parcours d’un tableau
for (JsonNode tag : tags) {
System.out.println(tag.asText());
}
// Sortie :
// java
// json
Objets imbriqués
Exemple de JSON complexe :
{
"user": {
"id": 1,
"profile": {
"nickname": "java_guru",
"age": 25
}
}
}
Extraction d’une valeur imbriquée :
JsonNode profile = root.get("user").get("profile");
String nickname = profile.get("nickname").asText();
System.out.println(nickname); // java_guru
Accès sécurisé : get vs path
- get("clé") — s’il n’y a pas de clé, renvoie null. Tout appel comme asText() sur null entraînera un NullPointerException.
- path("clé") — s’il n’y a pas de clé, renvoie un nœud « vide », et asText() renverra "", asInt() — 0.
String phone = root.path("phone").asText(); // "" si le champ est absent
Vérification du type de nœud
if (root.has("tags") && root.get("tags").isArray()) {
for (JsonNode tag : root.get("tags")) {
// ...
}
}
Modification JsonNode
JsonNode est immuable. Pour créer/modifier un arbre, utilisez ObjectNode/ArrayNode.
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
ObjectMapper mapper = new ObjectMapper();
// Création d’un nouvel objet
ObjectNode obj = mapper.createObjectNode();
obj.put("id", 10);
obj.put("name", "Bob");
// Ajout d’un tableau
ArrayNode arr = mapper.createArrayNode();
arr.add("Java").add("JSON");
obj.set("tags", arr);
System.out.println(obj.toPrettyString());
/*
{
"id" : 10,
"name" : "Bob",
"tags" : [ "Java", "JSON" ]
}
*/
3. Travail avec Gson: JsonElement, JsonObject, JsonArray
Jackson n’est pas le seul acteur. Gson fournit également une API pratique pour le JSON dynamique : JsonElement, JsonObject, JsonArray.
Analyse dans JsonElement
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
String json = "{\"id\":123,\"name\":\"Alice\",\"tags\":[\"java\",\"json\"]}";
JsonElement root = JsonParser.parseString(json);
Accès aux champs
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
JsonObject obj = root.getAsJsonObject();
int id = obj.get("id").getAsInt();
String name = obj.get("name").getAsString();
JsonArray tags = obj.getAsJsonArray("tags");
for (JsonElement tag : tags) {
System.out.println(tag.getAsString());
}
Imbrication et sécurité
if (obj.has("email")) {
String email = obj.get("email").getAsString();
}
Manipulation des tableaux
String arrJson = "[{\"id\":1},{\"id\":2}]";
JsonArray arr = JsonParser.parseString(arrJson).getAsJsonArray();
for (JsonElement el : arr) {
JsonObject item = el.getAsJsonObject();
System.out.println(item.get("id").getAsInt());
}
Modification
Dans Gson, les objets sont mutables — on peut ajouter et supprimer des champs :
import com.google.gson.JsonObject;
JsonObject newObj = new JsonObject();
newObj.addProperty("id", 42);
newObj.add("tags", tags);
System.out.println(newObj.toString());
4. Pratique : extraire des données à partir d’un JSON inconnu
Objectif : Supposons que nous disposions d’une configuration JSON dont la structure peut changer :
{
"service": "mail",
"enabled": true,
"params": {
"host": "smtp.example.com",
"port": 587
}
}
Extraire host et port avec Jackson :
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
JsonNode params = root.path("params");
String host = params.path("host").asText();
int port = params.path("port").asInt();
System.out.println(host + ":" + port); // smtp.example.com:587
La même chose avec Gson :
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
JsonObject rootObj = JsonParser.parseString(json).getAsJsonObject();
JsonObject params = rootObj.getAsJsonObject("params");
String host = params.get("host").getAsString();
int port = params.get("port").getAsInt();
System.out.println(host + ":" + port);
5. Erreurs courantes et nuances
Erreur n°1 : conversion de types erronée. Si vous attendez une chaîne, mais que le champ contient un nombre ou null, un appel à asText() ou getAsString() peut produire un résultat inattendu ou une exception. Par exemple, si le champ est absent, root.get("foo").asText() entraînera un NullPointerException.
Erreur n°2 : absence de vérification de null. Particulièrement lors d’accès imbriqués : root.get("params").get("host"). Si params est absent — vous aurez un NPE. Dans Jackson, utilisez path(), dans Gson — vérifiez la présence via has et le type via isJsonObject()/isJsonArray().
Erreur n°3 : confusion sur les types de nœuds. Si un champ est un tableau et que vous l’utilisez comme un objet — vous obtiendrez une erreur. Vérifiez les types : Jackson — isArray()/isObject(), Gson — isJsonArray()/isJsonObject().
Erreur n°4 : perte d’information de type lors de l’utilisation de Map/List. Lors d’une désérialisation en Map<String, Object>, les structures imbriquées deviennent une cascade de Map/List, ce qui complique la navigation et conduit à de nombreux casts et vérifications instanceof.
Erreur n°5 : mauvaise compréhension de l’(im)mutabilité de l’arbre. Dans Jackson, JsonNode est immuable, et les modifications doivent se faire via ObjectNode et ArrayNode. Dans Gson, les objets sont mutables ; gardez à l’esprit que la modification d’un nœud affecte toutes les références à celui-ci dans l’arbre courant.
GO TO FULL VERSION