Principes de sérialisation JSON en Java
Le format JSON s'est imposé comme le standard d'échange de données dans les architectures distribuées. En Java, la transformation entre chaînes JSON et structures natives (objets métier, collections, tableaux) nécessite généralement l'intervention de bibliothèques de mappage. Ce document détaille les mécanismes de conversion via deux approches : l'API historique json-lib et le moteur moderne Jackson.
1. Manipulation avec json-lib
La bibliothèque json-lib repose principalement sur les classes JSONObject et JSONArray. Bien que moins utilisée aujourd'hui, elle illustre clairement la distinction structurelle entre un objet JSON {} et un tableau JSON [].
Conversion Bean vers JSON
Un objet Java standard peut être sérialisé indifféremment en objet ou en tableau JSON. La structure de sortie dépend de la classe utilitaire invoquée.
public class DemoJsonLib {
public static void serialiserBean() {
ProfilUtilisateur profil = new ProfilUtilisateur();
profil.setIdentifiant("U001");
profil.setNom("Dupont");
profil.setLocalisation("Lyon");
JSONObject noeudObjet = JSONObject.fromObject(profil);
JSONArray noeudTableau = JSONArray.fromObject(profil);
System.out.println("Format objet : " + noeudObjet.toString());
System.out.println("Format tableau : " + noeudTableau.toString());
}
}
Résultat en console :
Format objet : {"identifiant":"U001","localisation":"Lyon","nom":"Dupont"}
Format tableau : [{"identifiant":"U001","localisation":"Lyon","nom":"Dupont"}]
Conversion JSON vers Bean
La désérialisation nécessite de parser la chaîne source, puis d'appliquer un mappage vers la classe cible. Pour les tableaux, il faut extraire l'élément avant la conversion.
public static void deserialiserVersBean() {
String jsonSimple = "{\"identifiant\":\"U002\",\"nom\":\"Martin\",\"localisation\":\"Paris\"}";
String jsonTableau = "[{\"identifiant\":\"U003\",\"nom\":\"Bernard\",\"localisation\":\"Marseille\"}]";
JSONObject objetParse = JSONObject.fromObject(jsonSimple);
ProfilUtilisateur p1 = (ProfilUtilisateur) JSONObject.toBean(objetParse, ProfilUtilisateur.class);
JSONArray tableauParse = JSONArray.fromObject(jsonTableau);
JSONObject premierItem = (JSONObject) tableauParse.get(0);
ProfilUtilisateur p2 = (ProfilUtilisateur) JSONObject.toBean(premierItem, ProfilUtilisateur.class);
System.out.println(p1);
System.out.println(p2);
}
Gestion des Listes et Maps
Les collections Java sont naturellement mappées vers des tableaux JSON. L'opération inverse exige une conversion explicite via les méthodes dédiées de JSONArray.
public static void gererCollections() {
// Liste vers JSON
List<profilutilisateur> listeProfils = new ArrayList<>();
listeProfils.add(new ProfilUtilisateur("U004", "Leroy", "Bordeaux"));
JSONArray jsonListe = JSONArray.fromObject(listeProfils);
System.out.println("Liste sérialisée : " + jsonListe.toString());
// JSON vers Liste
String sourceJson = "[{\"identifiant\":\"U005\",\"nom\":\"Moreau\",\"localisation\":\"Nantes\"}]";
JSONArray tableauSource = JSONArray.fromObject(sourceJson);
List<profilutilisateur> resultatListe = (List<profilutilisateur>) JSONArray.toList(tableauSource, ProfilUtilisateur.class);
resultatListe.forEach(System.out::println);
// Map vers JSON
Map<string profilutilisateur=""> carteProfils = new HashMap<>();
carteProfils.put("admin", new ProfilUtilisateur("U006", "Dubois", "Lille"));
JSONObject jsonMap = JSONObject.fromObject(carteProfils);
System.out.println("Map sérialisée : " + jsonMap.toString());
}</string></profilutilisateur></profilutilisateur></profilutilisateur>
Pour restaurer une structure associative complexe depuis une chaîne JSON, json-lib impose souvent l'usage d'un contenuer intermédiaire ou d'un dicitonnaire de types lors de l'appel à toBean(), afin de résoudre les génériques à l'exécution.
2. Approche moderne avec Jackson (ObjectMapper)
Jackson est la référence actuelle pour le traitement JSON en Java. La classe centrale ObjectMapper gère automatiquement l'introspection, les génériques et les structures imbriquées sans configuration verbeuse.
Sérialisation de structures composites
La méthode writeValueAsString() accepte n'importe quelle structure Java et produit une représentation JSON optimisée.
public class MoteurJackson {
private static final ObjectMapper CONVERTISSEUR = new ObjectMapper();
public static void exporterVersJson() throws JsonProcessingException {
List<contact> annuaire = Arrays.asList(
new Contact("alice", "alice@domaine.fr", 28),
new Contact("bob", "bob@domaine.fr", 34)
);
Map<string object=""> paquetDonnees = new HashMap<>();
paquetDonnees.put("contacts", annuaire);
paquetDonnees.put("parametres", Map.of("version", "2.1", "actif", true));
paquetDonnees.put("indices", new int[]{10, 25, 40});
String fluxJson = CONVERTISSEUR.writeValueAsString(paquetDonnees);
System.out.println(fluxJson);
}
}</string></contact>
Sortie générée :
{"contacts":[{"pseudo":"alice","courriel":"alice@domaine.fr","age":28},{"pseudo":"bob","courriel":"bob@domaine.fr","age":34}],"parametres":{"version":"2.1","actif":true},"indices":[10,25,40]}
Désérialisation typée et sécurisée
Contrairement aux approches brutes, Jackson recommande l'utilisation de TypeReference pour préserver les informatoins de type générique lors de la lecture de listes ou de maps.
public static void importerDepuisJson() throws IOException {
String jsonListe = "[{\"pseudo\":\"charlie\",\"courriel\":\"c@test.fr\",\"age\":22},{\"pseudo\":\"dave\",\"courriel\":\"d@test.fr\",\"age\":29}]";
String jsonMap = "{\"configuration\":{\"delai\":500,\"environnement\":\"production\"},\"statut\":\"valide\"}";
// Lecture typée d'une liste
List<contact> contactsRestitues = CONVERTISSEUR.readValue(
jsonListe,
new TypeReference<list>>(){}
);
// Lecture typée d'une map imbriquée
Map<string object=""> configRestituee = CONVERTISSEUR.readValue(
jsonMap,
new TypeReference<map object="">>(){}
);
contactsRestitues.forEach(System.out::println);
configRestituee.forEach((cle, valeur) -> System.out.println(cle + " => " + valeur));
}</map></string></list></contact>
Conversion vers tableaux et objets simples
Le mappage vers des tableaux Java ou des beans isolés suit la même logique, en spécifiant directement la classe cible ou le type tableau.
public static void convertirTypesSimples() throws IOException {
String jsonBean = "{\"pseudo\":\"eve\",\"courriel\":\"e@reseau.fr\",\"age\":31}";
String jsonTableau = "[{\"pseudo\":\"franck\",\"courriel\":\"f@reseau.fr\",\"age\":40},{\"pseudo\":\"gina\",\"courriel\":\"g@reseau.fr\",\"age\":27}]";
Contact contactUnique = CONVERTISSEUR.readValue(jsonBean, Contact.class);
Contact[] tableauContacts = CONVERTISSEUR.readValue(jsonTableau, Contact[].class);
System.out.println("Bean isolé : " + contactUnique);
System.out.println("Nombre d'éléments dans le tableau : " + tableauContacts.length);
}
L'API Jackson gère nativement les correspondances de noms de champs, les types numériques, les booléens et les structures nulles. Pour les scénarios avancés, des annotations comme @JsonProperty ou @JsonIgnore permettent d'affiner le comportement de sérialisation sans altérer la logique métier.