Java JSON avec Jackson
Analysez, générez et liez du JSON en Java avec la bibliothèque Jackson — ObjectMapper et data binding.
Jackson est la bibliothèque JSON de référence pour Java. Le JDK ne fournit aucune API JSON native, aussi presque toutes les applications Spring, Quarkus ou Micronaut utilisent Jackson pour convertir des objets Java en texte JSON et inversement. Son point d'entrée est une seule classe polyvalente — ObjectMapper — qui gère les deux opérations toujours nécessaires : la sérialisation (objet Java → JSON) et la désérialisation (JSON → objet Java).
Si vous débutez avec JSON en Java, commencez par l'introduction au JSON ; pour une bibliothèque alternative plus légère, consultez le chapitre sur Gson. Cette page couvre l'ajout de Jackson, les trois niveaux de son API, le data binding, le modèle arborescent et les annotations qui contrôlent le mapping.
Ajouter Jackson à votre projet
Jackson ne fait pas partie du JDK, il faut donc le déclarer comme dépendance. L'artefact jackson-databind tire les deux autres modules principaux (jackson-core et jackson-annotations) de manière transitive.
<!-- Maven -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.1</version>
</dependency>// Gradle
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'Les trois façons de travailler avec JSON
Jackson expose les mêmes données de trois manières différentes. Choisissez le niveau adapté à la tâche :
| Approche | Type principal | Quand l'utiliser |
|---|---|---|
| Data binding | ObjectMapper.readValue / writeValue | Vous avez un POJO ou un record qui reflète le JSON — le cas le plus courant |
| Modèle arborescent | JsonNode via readTree | La structure est dynamique ou vous n'avez besoin que de quelques champs |
| Streaming | JsonParser / JsonGenerator | Documents volumineux qu'on ne peut pas charger entièrement en mémoire |
Le data binding est utilisé 90 % du temps ; les deux autres sont des solutions de secours.
Data binding : des objets en entrée, du JSON en sortie
ObjectMapper.writeValueAsString sérialise tout objet Java en lisant ses getters (ou ses composants de record) ; readValue fait l'inverse, en associant les noms des champs JSON à vos champs. Un record est la cible la plus propre — Jackson 2.12+ se lie à son constructeur canonique automatiquement, sans avoir à écrire de getters ni de constructeur sans argument.
import com.fasterxml.jackson.databind.ObjectMapper;
record User(String name, int age, boolean active) {}
ObjectMapper mapper = new ObjectMapper();
// serialize: Java -> JSON
User ada = new User("Ada", 36, true);
String json = mapper.writeValueAsString(ada);
// {"name":"Ada","age":36,"active":true}
// deserialize: JSON -> Java
User back = mapper.readValue(json, User.class);
System.out.println(back.name()); // AdaPour les collections et les génériques, passez à Jackson une TypeReference afin que le type d'élément survive à l'effacement de type :
import com.fasterxml.jackson.core.type.TypeReference;
List<User> users = mapper.readValue(jsonArray, new TypeReference<List<User>>() {});Le modèle arborescent : quand la structure est inconnue
Lorsque vous n'avez pas (ou ne souhaitez pas) de classe correspondante, analysez le JSON dans un arbre générique JsonNode et naviguez-y par clé. Cela ressemble à ce que ferait un parseur Map/List artisanal, mais avec des accesseurs typés comme asInt() et asText().
import com.fasterxml.jackson.databind.JsonNode;
JsonNode root = mapper.readTree(json);
String name = root.get("name").asText();
int age = root.get("age").asInt();
JsonNode first = root.get("languages").get(0); // array access by indexContrôler le mapping avec les annotations
Les noms de champs JSON correspondent rarement parfaitement aux conventions Java. Quelques annotations comblent cet écart sans modifier vos noms de champs :
| Annotation | Effet |
|---|---|
@JsonProperty("user_name") | Associer un champ à une clé JSON différente |
@JsonIgnore | Exclure un champ dans les deux sens |
@JsonInclude(NON_NULL) | Supprimer les champs null de la sortie |
@JsonCreator / @JsonFormat | Construction personnalisée / formatage de date |
record Account(
@JsonProperty("user_name") String userName,
@JsonIgnore String passwordHash) {}Un exemple complet : sérialiser, analyser et lier manuellement
Jackson n'est pas disponible sur le classpath de ce runner, le programme ci-dessous reproduit donc les mêmes concepts — un writer JSON, un parseur récursif dans un arbre Map/List, et la liaison dans un record — en utilisant uniquement java.util. C'est exactement ce que fait ObjectMapper sous le capot : parcourir un graphe d'objets pour émettre du texte, et tokeniser le texte pour reconstruire un arbre.
Ce qu'il faut retenir de l'exécution :
- La sérialisation parcourt un graphe d'objets et émet du texte. La ligne
serializedmontre unMap/Listimbriqué rendu en{"name":"Ada",...,"languages":["Java","Ada"]}— un tableau dans un objet.ObjectMapper.writeValueAsStringeffectue le même parcours récursif sur les getters de votre POJO ou les composants d'un record. - L'analyse reconstruit d'abord un arbre générique. Le type d'exécution de la valeur analysée est
LinkedHashMap, pasUser— exactement le modèle arborescent de Jackson, oùreadTreevous donne unJsonNodeque vous naviguez par clé avant qu'une classe soit impliquée. - Les nombres JSON deviennent des nombres Java, avec un choix de type. Le champ
ageest revenu commeInteger(42) parce que le texte n'avait pas de virgule décimale ; le parseur a choisiIntegerplutôt queDouble. Jackson fait le même choix, c'est pourquoi un champintse lie proprement tandis que3.14atterrirait commeDouble. - L'accès aux champs se fait par nom, et l'ordre est préservé. L'utilisation de
LinkedHashMapa conservé les clés dans l'ordre d'insertion, doncnameest lu avantage. Jackson préserve l'ordre des clés d'objet de la même façon, c'est pourquoi le JSON après un aller-retour ressemble à l'original. - L'aller-retour est sans perte quand le modèle correspond. La dernière ligne a re-sérialisé l'arbre analysé vers le même JSON d'origine, et le record typé
Usera lié correctementname,ageetlanguages— tout l'intérêt du data binding : texte en entrée, objet en sortie, texte à nouveau, sans dérive.