IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

 

Développons en Java   2.30  
Copyright (C) 1999-2022 Jean-Michel DOUDOUX    (date de publication : 15/06/2022)

[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

 

57. JSON-B (Java API for JSON Binding)

 

chapitre    5 7

 

Niveau : niveau 3 Intermédiaire 
Version utilisée : 1.0 

 

Il existe différentes solutions open source (Jackson, Genson, Gson, ...) pour réaliser le binding entre des documents JSON et des objets Java mais JSON-B propose une API standard pour permettre ses opérations.

Java API for JSON Binding (JSON-B) est spécifié dans la JSR 367.

Le site web officiel est à l'url https://javaee.github.io/jsonb-spec/

L'API JSON-B permet de réaliser des opérations de sérialisation et de désérialisation entre des documents JSON et des objets Java :

  • La désérialisation est un traitement qui lit un document JSON pour construire un objet ou un graphe d'objets qui encapsule les données du document
  • La sérialisation est le processus inverse qui permet de produire un document JSON à partir d'un objet

L'implémentation de référence de JSON-B 1.0 est Yasson

Les conversions de JSON-B se font avec un comportement par défaut mais il est possible de les personnaliser.

Ce chapitre contient plusieurs sections :

 

57.1. La sérialisation/désérialisation d'un objet

La mise en oeuvre de JSON-B pour le binding de documents JSON est similaire à celle de JAX-B pour le binding de documents XML.

Des règles de conversions par défaut sont utilisées lors des opérations exécutées par JSON-B. Il est possible de personnaliser ces règles en utilisant des annotations ou un objet de type JsonbConfig pour des règles globales.

Les classes et interfaces de l'API JSON-B sont dans le package javax.json.bind et ses sous-packages.

Pour utiliser Yasson, l'implémentation de référence de JSON-B 1.0, en dehors d'un serveur d'applications Java EE 8, il faut ajouter plusieurs dépendances :

Exemple avec Maven :
  <dependencies>
    <!-- JSON-P API -->
    <dependency>
      <groupId>javax.json</groupId>
      <artifactId>javax.json-api</artifactId>
      <version>1.1</version>
    </dependency>

    <!-- JSON-B API -->
    <dependency>
      <groupId>javax.json.bind</groupId>
      <artifactId>javax.json.bind-api</artifactId>
      <version>1.0</version>
    </dependency>

    <!-- JSON-B RI -->
    <dependency>
      <groupId>org.eclipse</groupId>
      <artifactId>yasson</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>

    <!-- JSON-P RI -->
    <dependency>
      <groupId>org.glassfish</groupId>
      <artifactId>javax.json</artifactId>
      <version>1.1</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>

Il est ainsi possible de sérialiser/désérialiser de simples POJO.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
 
public class Personne {
 
  private String    nom;
  private String    prenom;
  private int       taille;
  private boolean   adulte;
  private LocalDate dateNaissance;
 
  // getters et setters 

}

Pour sérialiser une instance, il suffit de la passer en paramètre de la méthode toJson() d'une instance de type Jsonb préalablement obtenue.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
import java.time.Month;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Personne pers = new Personne();
    pers.nom = "nom";
    pers.prenom = "prenom";
    pers.taille = 175;
    pers.adulte = true;
    pers.dateNaissance = LocalDate.of(1985, Month.AUGUST, 11);
 
    Jsonb jsonb = JsonbBuilder.create();
    String result = jsonb.toJson(pers);
 
    System.out.println(result);
  }
}
Résultat :
{"adulte":true,"dateNaissance":"1985-08-11","nom":"nom","prenom":"prenom","taille":175}

La désérialisation se fait aussi de manière très simple : il suffit de passer le document JSON en paramètre de la méthode fromJson() d'une instance de type Jsonb préalablement obtenue.

Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Personne pers = new Personne();
    Jsonb jsonb = JsonbBuilder.create();
 
    pers = jsonb.fromJson("{\"nom\":\"nom1\",\"prenom\":\"prenom1\",\"taille\":183}",
        Personne.class);
    System.out.println("nom=" + pers.nom);
    System.out.println("prenom=" + pers.prenom);
    System.out.println("taille=" + pers.taille);
  }
}
Résultat :
nom=nom1
prenom=prenom1
taille=183

 

57.2. Le moteur JSON-B

L'interface JsonbBuilder est le point d'entrée pour utiliser l'API JSON-B.

Elle définit plusieurs méthodes mettant en oeuvre le design pattern builder dont le but est de créer une instance de type Jsonb en fonction des paramètres et de la configuration fournie avant l'invocation de la méthode build() :

Méthode

Rôle

Jsonb build()

Obtenir l'instance à partir du builder

static Jsonb create()

Obtenir une nouvelle instance de Jsonb obtenue à partir du JsonbBuilder de l'implémentation par défaut

static Jsonb create(JsonbConfig config)

Créer une nouvelle instance en utilisant l'implémentation par défaut de JsonbBuilder et la configuration passée en paramètre

static JsonbBuilder newBuilder()

Obtenir une instance de type JsonbBuilder obtenue grâce au fournisseur par défaut

static JsonbBuilder newBuilder(String providerName)

Obtenir une instance de type JsonbBuilder obtenue grâce au fournisseur dont le nom est passé en paramètre

static JsonbBuilder newBuilder(JsonbProvider provider)

Obtenir une instance de type JsonbBuilder obtenue grâce au fournisseur passé en paramètre

JsonbBuilder withConfig(JsonbConfig config)

Préciser la configuration de l'instance qui sera obtenue

JsonbBuilder withProvider(javax.json.spi.JsonProvider jsonpProvider)

Préciser le fournisseur de l'implémentation de JSON-B à utiliser


Si le comportement par défaut du moteur JSON-B respecte les besoins, il n'y a aucune configuration particulière à utiliser. L'obtention d'une instance configurée par défaut peut facilement être réalisée en invoquant la méthode create() de la classe JsonbBuilder.

Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Jsonb jsonb = JsonbBuilder.create(); 
  }
}

Il est recommandé de n'avoir qu'une seule instance de type Jsonb par configuration de mapping d'autant que les instances de type Jsonb doivent être thread safe.

L'interface Jsonb permet de demander des sérialisations et des désérialisations au moteur JSON-B. Elle définit plusieurs surcharges de deux méthodes :

Méthode

Rôle

<T> T fromJson(String str, Class<T> type);
<T> T fromJson(String str, Type runtimeType);
<T> T fromJson(Reader reader, Class<T> type);
<T> T fromJson(Reader reader, Type runtimeType);
<T> T fromJson(InputStream stream, Class<T> type);
<T> T fromJson(InputStream stream, Type runtimeType);

Convertir un document JSON en un objet Java

String toJson(Object object);
String toJson(Object object, Type runtimeType);
void toJson(Object object, Writer writer);
void toJson(Object object, Type runtimeType, Writer writer);
void toJson(Object object, OutputStream stream);
void toJson(Object object, Type runtimeType, OutputStream stream);

Convertir un objet Java en un document JSON


L'interface Jsonb hérite de l'interface AutoCloseable.

Le moteur JSON-B utilise un ensemble de règles pour réaliser les mapping.

 

57.3. Le mapping par défaut

Les implémentations de JSON-B doivent assurer la correspondance pour des documents JSON qui respectent la RFC 7159 (The JavaScript Object Notation (JSON) Data Interchange Format).

Le document JSON issue d'une sérialisation doit donc respecter la RFC 7159 et être encodé en UTF-8.

Le mapping par défaut ne requiert ni configuration ni annotation particulière. Ce mapping par défaut comporte des règles pour la sérialisation/désérialisation pour les principaux types :

  • Les types primitifs au travers de leur wrapper
  • Certains types du JDK
  • Les dates
  • Les tableaux
  • Les collections
  • Les énumérations
  • Des classes de JSON-P
  • Les classes qui encapsulent des données des types précédents

 

57.3.1. Le mapping par défaut des types communs

Une implémentation de JSON-B doit prendre en charge les types de base et les types spécifiques du JDK ci-dessous :

Type de base

Type spécifique du JDK

java.lang.String
java.lang.Character
java.lang.Byte (byte)
java.lang.Short (short)
java.lang.Integer (int)
java.lang.Long (long)
java.lang.Float (float)
java.lang.Double (double)
java.lang.Boolean (boolean)
java.lang.Number

java.math.BigInteger
java.math.BigDecimal
java.net.URL
java.net.URI
java.util.Optional
java.util.OptionalInt
java.util.OptionalLong
java.util.OptionalDouble


Exemple ( code Java 8 ) :
public class Primitives {
 
  public String  unString  = "string";
  public char    unChar    = 'a';
  public byte    unByte    = 1;
  public short   unShort   = 2;
  public int     unInt     = 3;
  public long    unLong    = 4L;
  public float   unFloat   = 5.1f;
  public double  unDouble  = 6.2;
  public boolean unBoolean = true;
}
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    JsonbConfig config = (new JsonbConfig()).withFormatting(true);
    Jsonb jsonb = JsonbBuilder.create(config);
    Primitives primitives = new Primitives();
    String result = jsonb.toJson(primitives);
    System.out.println(result);
  }
}
Résultat :
{
    "unString": "string",
    "unBoolean": true,
    "unByte": 1,
    "unChar": "a",
    "unDouble": 6.2,
    "unFloat": 5.1,
    "unInt": 3,
    "unLong": 4,
    "unShort": 2
}

Lors de la sérialisation, la valeur d'une instance de type BigInteger, BigDecimal, URL et URI est obtenue en invoquant leur méthode toString(). La désérialisation utilise leur constructeur qui attend une chaîne de caractères en paramètre.

La valeur sérialisée pour les Optionalxxx est la valeur encapsulée si elle est présente ou null si elle est vide.

Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    JsonbConfig config = (new JsonbConfig()).withFormatting(true);
    Jsonb jsonb = JsonbBuilder.create(config);
    Objets objets = new Objets();
    String result = jsonb.toJson(objets);
    System.out.println(result);
  }
}
Exemple ( code Java 8 ) :
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.OptionalInt;
 
public class Objets {
 
  public BigInteger  bigInteger    = BigInteger.valueOf(1000L);
  public BigDecimal  bigDecimal    = new BigDecimal("1.2");
  public OptionalInt optionalInt   = OptionalInt.of(1);
  public OptionalInt optionalEmpty = OptionalInt.empty();
  public URL         url           = null;
 
  public Objets() {
    try {
      url = new URL("file://c:/temp");
    } catch (MalformedURLException e) {
      e.printStackTrace();
    }
  }
}
Résultat :
{
    "bigDecimal": 1.2,
    "bigInteger": 1000,
    "optionalInt": 1,
    "url": "file://c:/temp"
}

Une implémentation de JSON-B doit prendre en charge les types temporels en sérialisant leur valeur selon le tableau ci-dessous :

java.util.Date, java.util.Calendar, java.util.GregorianCalendar

ISO_DATE ou ISO_DATE_TIME selon la présence d'informations sur l'heure

java.util.TimeZone, java.util.SimpleTimeZone

NormalizedCustomId (cf Javadoc de la classe TimeZone)

java.time.Instant

ISO_INSTANT

java.time.LocalDate

ISO_LOCAL_DATE

java.time.LocalTime

ISO_LOCAL_TIME

java.time.LocalDateTime

ISO_LOCAL_DATE_TIME

java.time.ZonedDateTime

ISO_ZONED_DATE_TIME

java.time.OffsetDateTime

ISO_OFFSET_DATE_TIME

java.time.OffsetTime

ISO_OFFSET_TIME

java.time.ZoneId

NormalizedZoneId (cf Javadoc de la classe ZoneId)

java.time.ZoneOffset

NormalizedZoneId (cf Javadoc de la classe ZoneOffset)

java.time.Duration

Représentation en secondes définie par l'ISO 8601

java.time.Period

Représentation période définie par l'ISO 8601


Exemple ( code Java 8 ) :
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Month;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.ZonedDateTime;
import java.util.Calendar;
import java.util.Date;
 
public class Dates {
 
  SimpleDateFormat      sdf            = new SimpleDateFormat("dd.MM.yyyy");
  public Date           date           = null;
 
  public Calendar       calendar       = Calendar.getInstance();
 
  public LocalDate      localDate      = LocalDate.now();
  public LocalTime      localTime      = LocalTime.now();
  public LocalDateTime  localDateTime  = LocalDateTime.now();
 
  public ZonedDateTime  zonedDatetime  = ZonedDateTime.now();
  public OffsetDateTime offsetDateTime = OffsetDateTime.now();
 
  public Duration       duree          = Duration.ofHours(1).plusMinutes(30);
  public Instant        instant        = Instant.parse("2017-08-27T12:00:00Z");
 
  public Period         periode        = Period.between(LocalDate.of(2017,
      Month.DECEMBER, 25), LocalDate.of(2016, Month.DECEMBER, 25));
 
  public Dates() {
    calendar.clear();
    calendar.set(2017, 7, 26);
 
    try {
      date = sdf.parse("15.11.2016");
    } catch (ParseException e) {
      e.printStackTrace();
    }
  }
}
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    JsonbConfig config = (new JsonbConfig()).withFormatting(true);
    Jsonb jsonb = JsonbBuilder.create(config);
    Dates dates = new Dates();
    String result = jsonb.toJson(dates);
    System.out.println(result);
  }
}
Résultat :
{
    "calendar": "2017-08-26+02:00",
    "date": "2016-11-14T23:00:00Z[UTC]",
    "duree": "PT1H30M",
    "instant": "2017-08-27T12:00:00Z",
    "localDate": "2017-08-27",
    "localDateTime": "2017-08-27T15:24:48.241",
    "localTime": "15:24:48.241",
    "offsetDateTime": "2017-08-27T15:24:48.242+02:00",
    "periode": "P-1Y",
    "zonedDatetime": "2017-08-27T15:24:48.241+02:00[Europe/Paris]"
}

Le mapping par défaut d'une énumération suit plusieurs règles :

  • La sérialisation utilise la méthode name() pour obtenir la valeur
  • La désérialisation utilise la méthode valueOf()
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  enum Taille { PETIT, MOYEN, GRAND };
 
  public static void main(String[] args) {
 
    Jsonb jsonb = JsonbBuilder.create();
    String result = jsonb.toJson(Taille.GRAND);
    System.out.println(result);
  }
}
Résultat :
"GRAND"

 

57.3.2. Le mapping par défaut d'un type quelconque

Le mapping par défaut d'un type quelconque suit plusieurs règles :

  • Les classes imbriquées public et protected sont supportées
  • Les classes anonymes sont uniquement sérialisées
  • Pour la désérialisation, les classes doivent avoir un constructeur par défaut (sans paramètre)
  • L'héritage est supporté

Le mapping par défaut d'un champ suit plusieurs règles :

  • Les champs finaux sont sérialisés
  • Les champs static ne sont pas sérialisés
  • Les champs transient ne sont pas sérialisés
  • Les champs null ne sont pas sérialisés
  • L'ordre de sérialisation des champs est l'ordre lexicographique. Les champs de la classe parente sont sérialisés avant ceux de la classe

Une implémentation de JSON-B doit respecter un certain ordre pour accéder ou alimenter des valeurs lors des opérations.

Pour la sérialisation :

  • Le getter public d'un champ
  • Un champ public sans getter
  • Un getter sans champ

Pour la désérialisation :

  • Le setter public d'un champ
  • Un champ public sans setter
  • Un setter sans champ
Exemple ( code Java 8 ) :
public class Donnees {
 
  public final int     champPublicFinal             = 1;
  private final int    champPublicPrivate           = 2;
  public static int    champPublicStatic            = 3;
  public int           champPublicSansGetter        = 4;
  public int           champPublicAvecGetterPrivate = 5;
  private int          champPrivateSansGetter       = 6;
  private int          champPrivateAvecGetter       = 7;
  public Integer       champPublicNull              = null;
  public transient int champPublicTransient         = 8;
 
  public int getSansChamp() {
    return 9;
  };
 
  public int getChampPrivateAvecGetter() {
    return 10;
  };
 
  private int getChampPublicAvecGetterPrivate() {
    return 11;
  }
}
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    Jsonb jsonb = JsonbBuilder.create();
    Donnees donnees = new Donnees();
    String result = jsonb.toJson(donnees);
    System.out.println(result);
  }
}
Résultat :
{"champPrivateAvecGetter":10,"champPublicFinal":1,"champPublicSansGetter":4,"sansChamp":9}

 

57.3.3. Le mapping par défaut des tableaux et des collections

Le mapping par défaut prend en compte les tableaux mono ou multi-dimensions et les principales classes de bases de l'API Collection : Collection, Map, Set, HashSet, NavigableSet, SortedSet, TreeSet, LinkedHashSet, HashMap, NavigableMap, SortedMap, TreeMap, LinkedHashMap, List, ArrayList, LinkedList, Deque, ArrayDeque, Queue, PriorityQueue.

Exemple ( code Java 8 ) :
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
public class Valeurs {
 
  public int[]                tableau = new int[5];
  public int[][]              tab2d   = { { 2, 4, 1 }, { 6, 8 }, { 7, 3, 6, 5 } };
  public List<Integer>        list    = new ArrayList<>();
  public Map<Integer, String> map     = new HashMap<>();
  public Set<Integer>         set     = new HashSet<>();
  public Deque<Integer>       deque   = new ArrayDeque<>();
}
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    Jsonb jsonb = JsonbBuilder.create();
 
    Valeurs valeurs = new Valeurs();
    valeurs.tableau[0] = 1;
    valeurs.tableau[1] = 2;
    valeurs.list.add(3);
    valeurs.list.add(null);
    valeurs.list.add(4);
    valeurs.map.put(1, "un");
    valeurs.map.put(2, "deux");
    valeurs.set.add(5);
    valeurs.set.add(5);
    valeurs.set.add(6);
    valeurs.deque.push(7);
    valeurs.deque.push(8);
    String result = jsonb.toJson(valeurs);
    System.out.println(result);
  }
}
Résultat :
{"deque":[8,7],"list":[3,null,4],"map":{"1":"un",
"2":"deux"},"set":[5,6],"tab2d":[[2,4,1],[6,8],[7,3,6,5]],"tableau":[1,2,0,0,0]}

Lors de la désérialisation d'un tableau JSON, il est nécessaire de préciser le type de l'instance retournée par la méthode fromJson(). Ce type peut être un tableau.

Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Jsonb jsonb = JsonbBuilder.create();
    String[] valeurs = jsonb.fromJson("[\"valeur1\",\"valeur2\",\"valeur3\"]",
        String[].class);
    for (String valeur : valeurs) {
      System.out.println(valeur);
    }
  }
}
Résultat :
valeur1
valeur2
valeur3

Ce type peut aussi être une collection.

Exemple ( code Java 8 ) :
import java.util.ArrayList;
import java.util.List;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Jsonb jsonb = JsonbBuilder.create();
    List valeurs = jsonb.fromJson("[\"valeur1\",\"valeur2\",\"valeur3\"]",
        ArrayList.class);
    for (Object object : valeurs) {
      System.out.println(object);
    }
  }
}

JSON-B supporte aussi les collections génériques. Pour que le moteur puisse réaliser la désérialisation, il faut passer en second paramètre de la méthode fromJson() le type de l'objet renvoyé.

Exemple ( code Java 8 ) :
import java.util.ArrayList;
import java.util.List;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Jsonb jsonb = JsonbBuilder.create();
    List<String> valeurs = jsonb.fromJson("[\"valeur1\",\"valeur2\",\"valeur3\"]",
        new ArrayList<String>() {}.getClass().getGenericSuperclass());
    for (String valeur : valeurs) {
      System.out.println(valeur);
    }
  }
}

 

57.3.4. Le mapping par défaut des classes de JSON-P

Le mapping par défaut peut utiliser des classes de l'API JSON-P :

  • javax.json.JsonArray
  • javax.json.JsonStructure
  • javax.json.JsonValue
  • javax.json.JsonPointer
  • javax.json.JsonString
  • javax.json.JsonNumber
  • javax.json.JsonObject

Le mapping par défaut des classes de l'API JSON-P suit plusieurs règles :

  • Les types JSON-P supportés sont : javax.json.JsonArray, javax.json.JsonStructure, javax.json.JsonValue, javax.json.JsonPointer, javax.json.JsonString, javax.json.JsonNumber
  • La sérialisation utilise la classe javax.json.JsonWriter
  • La désérialisation utilise la classe javax.json.JsonReader
Exemple ( code Java 8 ) :
import javax.json.Json;
import javax.json.JsonBuilderFactory;
import javax.json.JsonObject;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    Jsonb jsonb = JsonbBuilder.create();
    JsonBuilderFactory factory = Json.createBuilderFactory(null);
 
    JsonObject jsonObject = factory.createObjectBuilder()
        .add("nom", "nom1")
        .add("prenom", "prenom1")
        .add("dateNaissance", "1985-08-11")
        .add("taille", 175)
        .build();
 
    String result = jsonb.toJson(jsonObject);
    System.out.println(result);
  }
}
Résultat :
{"nom":"nom1","prenom":"prenom1","dateNaissance":"1985-08-11","taille":175}

 

57.4. La configuration du moteur JSON-B

Il est possible de modifier le comportement par défaut du moteur JSON-B, pour la sérialisation et la désérialisation, en utilisant une configuration particulière, encapsulée dans un objet de type javax.json.bind.JsonbConfig.

 

57.4.1. La classe JsonbConfig

La classe JsonbConfig met en oeuvre le design pattern builder pour permettre de construire un objet qui encapsule les options de configuration particulière du moteur JSON-B.

Elle définit plusieurs méthodes permettant de définir la configuration qui sera utilisée par le moteur JSON-B :

Méthode

Rôle

Map<String,Object> getAsMap()

Renvoyer une Map non modifiable de toutes les propriétés de configuration

Optional<Object> getProperty(String name)

Renvoyer la valeur de la propriété de configuration dont le nom est fourni en paramètre

JsonbConfig setProperty(String name, Object value)

Modifier la valeur de la propriété dont le nom est fourni en paramètre

JsonbConfig withAdapters(JsonbAdapter... adapters)

Enregistrer les adaptateurs fournis en paramètre

JsonbConfig withBinaryDataStrategy(String binaryDataStrategy)

Préciser la stratégie de conversion des données binaires

JsonbConfig withDateFormat(String dateFormat, Locale locale)

Préciser le format de conversion des dates

JsonbConfig withDeserializers(JsonbDeserializer... deserializers)

Enregistrer les Deserializers fournis en paramètres

JsonbConfig withEncoding(String encoding)

Préciser le jeu d'encodage des caractères des documents JSON

JsonbConfig withFormatting(Boolean formatted)

Préciser si le document JSON produit doit être formaté ou non

JsonbConfig withLocale(Locale locale)

Préciser la Locale à utiliser

JsonbConfig withNullValues(Boolean serializeNullValues)

Préciser si les propriétés ayant une valeur null sont sérialisées ou non

JsonbConfig withPropertyNamingStrategy(String propertyNamingStrategy)

Préciser la stratégie de nommage des propriétés parmi celles proposées en standard

JsonbConfig withPropertyNamingStrategy(PropertyNamingStrategy propertyNamingStrategy)

Préciser une stratégie personnalisée de nommage des propriétés

JsonbConfig withPropertyOrderStrategy(String propertyOrderStrategy)

Préciser la stratégie de gestion de l'ordre des propriétés

JsonbConfig withPropertyVisibilityStrategy(PropertyVisibilityStrategy propertyVisibilityStrategy)

Préciser une stratégie de gestion de la visibilité des propriétés

JsonbConfig withSerializers(JsonbSerializer... serializers)

Enregistrer des Serializers

JsonbConfig withStrictIJSON(Boolean enabled)

Préciser si le document JSON produit doit respecter la spécification I-JSON ou non


Pour utiliser la configuration, il faut passer une instance de JsonbConfig en paramètre de la méthode withConfig() de la classe JsonbBuilder.

Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
     JsonbConfig config = (new JsonbConfig()).withFormatting(true);
     Jsonb jsonb = JsonbBuilder.create(config);
  }
}

 

57.4.2. Le formatage du résultat de la sérialisation

Pour demander le formatage du résultat de la sérialisation, ce qui augmente la taille du document mais le rend plus facile à lire par un humain, il suffit de passer la valeur true à la méthode withFormatting() de la classe JsonbConfig.

Exemple ( code Java 8 ) :
    JsonbConfig config = new JsonbConfig().withFormatting(true);
    Jsonb jsonb = JsonbBuilder.create(config);
    String result = jsonb.toJson(pers);
    System.out.println(result);
Résultat :
{
    "adulte": true,
    "dateNaissance": "1985-08-11",
    "nom": "nom",
    "prenom": "prenom",
    "taille": 175
}

 

57.5. La personnalisation du mapping

De nombreux aspects du mapping peuvent être configurés :

  • Le nom des propriétés
  • L'ordre des propriétés
  • Ignorer des propriétés
  • La gestion des valeurs null
  • La création des instances
  • La visibilité des champs
  • Le format des dates et des nombres
  • L'encodage des données binaires
  • Les adaptateurs
  • Les Serializers/Deserializers

Cette configuration peut se faire soit :

  • En utilisant certaines annotations
  • En utilisant une instance de type JsonbConfig pour configurer l'instance de type Jsonb

 

57.5.1. Le nom d'une propriété

Par défaut, le nom de la propriété dans le document est le nom de propriété dans l'objet Java. Il est possible de personnaliser le nom d'une propriété en utilisant l'annotation @JsonbProperty pour changer son nom.

L'annotation @JsonbProperty peut être utilisée sur :

  • Sur un champ : le nom de la propriété est utilisé lors des sérialisations et des désérialisations
  • Sur un getter : le nom de la propriété indiqué est uniquement utilisé lors des sérialisations
  • Sur un setter : le nom de la propriété indiqué est uniquement utilisé lors des désérialisations

Pour modifier le nom de la propriété d'un champ lors la sérialisation ou la désérialisation, il faut utiliser l'annotation @JsonbProperty sur un champ en lui passant comme valeur le nom de la propriété.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
 
import javax.json.bind.annotation.JsonbProperty;
 
public class Personne {
 
  @JsonbProperty("nom-special")
  private String    nom;
  
  // ...

}
Résultat :
{"adulte":true,"dateNaissance":"1985-08-11","nom-special":"nom","prenom":"prenom","taille":175}

Il est possible d'utiliser l'annotation @JsonbProperty sur les getters/setters pour permettre d'utiliser un nom de propriété différent lors de la sérialisation et de la désérialisation.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
 
import javax.json.bind.annotation.JsonbProperty;
 
public class Personne {
 
  // ...

 
  @JsonbProperty("nom-serialise")
  public String getNom() {
    return nom;
  }
 
  @JsonbProperty("nom-deserialise")
  public void setNom(String nom) {
    this.nom = nom;
  }
  
  //...

}
Exemple ( code Java 8 ) :
import java.time.LocalDate;
import java.time.Month;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Personne pers = new Personne();
    pers.setNom("nom");
    pers.setPrenom("prenom");
    pers.setTaille(175);
    pers.setAdulte(true);
    pers.setDateNaissance(LocalDate.of(1985, Month.AUGUST, 11));
 
    Jsonb jsonb = JsonbBuilder.create();
    String result = jsonb.toJson(pers);
    System.out.println(result);
 
    pers = jsonb.fromJson(
        "{\"nom-deserialise\":\"nom1\",\"prenom\":\"prenom1\",\"taille\":183}",
        Personne.class);
    System.out.println("nom=" + pers.getNom());
  }
}
Résultat :
{"adulte":true,"dateNaissance":"1985-08-11","nom-serialise":"nom","prenom":"prenom","taille":
175}
nom=nom1

 

57.5.2. La stratégie de nommage des propriétés

Une stratégie de nommage de propriétés permet de définir d'une manière globale comment sont gérés les noms des propriétés.

Plusieurs stratégies de nommage sont proposées grâce à des constantes de l'interface PropertyNamingStrategy :

Stratégie de nommage

Exemple

IDENTITY

nomDeMaPropriete

LOWER_CASE_WITH_DASHES

nom-de-ma-propriete

LOWER_CASE_WITH_UNDERSCORES

nom_de_ma_propriete

UPPER_CAMEL_CASE

NomDeMaPropriete

UPPER_CAMEL_CASE_WITH_SPACES

Nom De Ma Propriete

CASE_INSENSITIVE

nOmDeMaPrOpRiEtE


Il est possible de définir et d'utiliser sa propre stratégie de nommage en implémentant l'interface JsonbNamingInterface.

La stratégie par défaut est IDENTITY.

Pour demander l'application d'une stratégie particulière, il faut utiliser une des deux surcharges de la méthode de la classe JsonbConfig :

  • JsonbConfig withPropertyNamingStrategy(String propertyNamingStrategy) : pour appliquer une des stratégies standard
  • JsonbConfig withPropertyNamingStrategy(PropertyNamingStrategy propertyNamingStrategy) : pour appliquer sa propre implémentation
Exemple ( code Java 8 ) :
    JsonbConfig config = new JsonbConfig().withPropertyNamingStrategy(
        PropertyNamingStrategy.LOWER_CASE_WITH_DASHES);
    Jsonb jsonb = JsonbBuilder.create(config);
    String result = jsonb.toJson(pers);
    System.out.println(result);
Résultat :
{"adulte":true,"date-naissance":"1985-08-11","nom":"nom","prenom":"prenom","taille":175}

 

57.5.3. Les stratégies de gestion de l'ordre des propriétés

Il est possible de personnaliser l'ordre dans lequel les propriétés sont sérialisées en utilisant la classe PropertyOrderStrategy. Elle définit trois constantes pour désigner les stratégies supportées :

  • LEXICOGRAPHICAL : l'ordre lexicographique
  • ANY : aucun ordre particulier
  • REVERSE : l'ordre lexicographique inverse

La stratégie de gestion de l'ordre de sérialisation des propriétés par défaut du moteur JSON-B est LEXICOGRAPHICAL. Il est possible de modifier ce comportement de deux manières :

  • Globalement en utilisant la méthode withPropertyOrderStrategy() de la classe JsonbConfig
  • Utiliser l'annotation @JsonbPropertyOrder sur une classe

Il est possible de modifier la configuration pour qu'elle utilise une stratégie d'ordonnancement des propriétés lors de la sérialisation en utilisant la méthode withPropertyOrderStrategy() de la classe JsonbConfig en lui passant en paramètre une des constantes définies dans la classe PropertyOrderStrategy.

Exemple ( code Java 8 ) :
   JsonbConfig config = new JsonbConfig().withPropertyOrderStrategy(PropertyOrderStrategy.ANY);
   Jsonb jsonb = JsonbBuilder.create(config);
   String result = jsonb.toJson(pers);
   System.out.println(result);
Résultat :
{"taille":175,"adulte":true,"dateNaissance":"1985-08-11","nom":"nom","prenom":"prenom"}

Pour appliquer une stratégie particulière à une classe, il faut lui appliquer l'annotation @JsonbPropertyOrder en lui passant comme valeur une des constantes définies dans la classe PropertyOrderStrategy

Exemple ( code Java 8 ) :
import java.time.LocalDate;
 
import javax.json.bind.annotation.JsonbPropertyOrder;
import javax.json.bind.config.PropertyOrderStrategy;
 
@JsonbPropertyOrder(PropertyOrderStrategy.ANY)
public class Personne {
 
  // ...

}

 

57.5.4. Ignorer une propriété

L'annotation @javax.json.bind.annotation.JsonbTransient permet de demander au moteur JSON-B d'ignorer une propriété lors de ses opérations de sérialisation et/ou de désérialisation selon l'élément sur lequel l'annotation est placée :

  • Sur un champ : la propriété est ignorée lors des sérialisations et des désérialisations
  • Sur un getter : la propriété est uniquement ignorée lors des sérialisations
  • Sur un setter : la propriété est uniquement ignorée lors des désérialisations
Exemple ( code Java 8 ) :
import java.time.LocalDate;
 
import javax.json.bind.annotation.JsonbTransient;
 
public class Personne {
 
  private String    nom;
  private String    prenom;
  private int       taille;
  private boolean   adulte;
 
  @JsonbTransient
  private LocalDate dateNaissance;
 
  // ...

 
  public LocalDate getDateNaissance() {
    return dateNaissance;
  }
 
  public void setDateNaissance(LocalDate dateNaissance) {
    this.dateNaissance = dateNaissance;
  }
}

Dans l'exemple ci-dessus, la propriété dateNaissance sera ignorée lors des opérations de sérialisation mais sera utilisée lors de désérialisations avec JSON-B.

Exemple ( code Java 8 ) :
  // ...

 
  private LocalDate dateNaissance;
 
  // ...

 
  public LocalDate getDateNaissance() {
    return dateNaissance;
  }
 
  @JsonbTransient
  public void setDateNaissance(LocalDate dateNaissance) {
    this.dateNaissance = dateNaissance;
  }
}

Dans l'exemple ci-dessus, la propriété dateNaissance sera ignorée lors des opérations de sérialisation et de désérialisation avec JSON-B.

Exemple ( code Java 8 ) :
  // ...

 
  private LocalDate dateNaissance;
 
  // ...

 
  public LocalDate getDateNaissance() {
    return dateNaissance;
  }
 
  public void setDateNaissance(LocalDate dateNaissance) {
    this.dateNaissance = dateNaissance;
  }
}

Dans l'exemple ci-dessus, la propriété dateNaissance sera utilisée lors des opérations de sérialisation et sera ignorée lors de désérialisations avec JSON-B.

 

57.5.5. La visibilité des propriétés

Il est possible de définir une stratégie personnalisée de visibilité des propriétés. Cela peut, par exemple, permettre de sérialiser des champs private sans getter.

Exemple ( code Java 8 ) :
public class MaClasse {
 
  private String description;
  private String nom;
  private int    longueur;
 
  public MaClasse() {
  }
 
  public MaClasse(String description, String nom, int longueur) {
    this.description = description;
    this.nom = nom;
    this.longueur = longueur;
  }
}

Il faut définir une classe qui implémente l'interface PropertyVisibilityStrategy. Cette interface définit deux méthodes :

Méthode

Rôle

boolean isVisible(Field field)

Renvoyer un booléen qui précise si le champ passé en paramètre doit être pris en compte comme étant une JsonbProperty

boolean isVisible(Method method)

Renvoyer un booléen qui précise si la méthode passée en paramètre doit être prise en compte comme étant une JsonbProperty


Exemple ( code Java 8 ) :
import java.lang.reflect.Field;
import java.lang.reflect.Method;
 
import javax.json.bind.config.PropertyVisibilityStrategy;
 
public class MonPropertyVisibilityStrategy implements PropertyVisibilityStrategy {
 
  @Override
  public boolean isVisible(Field field) {
    return true;
  }
 
  @Override
  public boolean isVisible(Method method) {
    return true;
  }
}

L'implémentation ci-dessus est très basique car elle permet d'accéder à toutes les propriétés.

Pour pouvoir utiliser la stratégie de manière globale, il faut utiliser la méthode withPropertyVisibilityStrategy() de la classe JsonbConfig utilisée pour obtenir l'instance du moteur JSON-B. Elle attend en paramètre une instance de la classe qui implémente l'interface PropertyVisibilityStrategy.

Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    MaClasse obj = new MaClasse("la description", "le nom", 250);
 
    JsonbConfig config = new JsonbConfig()
        .withPropertyVisibilityStrategy(new MonPropertyVisibilityStrategy());
    Jsonb jsonb = JsonbBuilder.create(config);
    String result = jsonb.toJson(obj);
    System.out.println(result);
  }
}
Résultat :
{"description":"la description","longueur":250,"nom":"le nom"}

Pour demander l'application de cette stratégie sur une seule classe, il faut lui appliquer l'annotation @JsonbVisibility en lui passant comme valeur de son attribut par le défaut la classe d'implémentation de l'interface PropertyVisibilityStrategy.

Exemple ( code Java 8 ) :
import javax.json.bind.annotation.JsonbVisibility;
 
@JsonbVisibility(MonPropertyVisibilityStrategy.class)
public class MaClasse {
 
  private String description;
  private String nom;
  private int    longueur;
 
  // ...

}
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    MaClasse obj = new MaClasse("la description", "le nom", 250);
 
    Jsonb jsonb = JsonbBuilder.create();
    String result = jsonb.toJson(obj);
    System.out.println(result);
  }
}
Résultat :
{"description":"la description","longueur":250,"nom":"le nom"}

 

57.5.6. La gestion des valeurs null

La configuration par défaut du moteur JSON-B ne sérialise pas les champs dont la valeur est null.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
import java.time.Month;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Personne pers = new Personne();
    pers.setNom(null);
    pers.setPrenom(null);
    pers.setTaille(175);
    pers.setAdulte(true);
    pers.setDateNaissance(LocalDate.of(1985, Month.AUGUST, 11));
 
    Jsonb jsonb = JsonbBuilder.create();
    String result = jsonb.toJson(pers);
    System.out.println(result);
  }
}
Résultat :
{"adulte":true,"dateNaissance":"1985-08-11","taille":175}

Il y a trois possibilités pour modifier ce comportement par défaut :

  • Utiliser l'annotation @JsonbNillable sur une classe ou un package
  • Utiliser l'annotation @JsonbProperty sur une propriété avec l'attribut nillable=true
  • Invoquer withNullValues(true) sur l'instance de JsonbConfig utilisée par le moteur pour changer globalement le comportement de la gestion des valeurs null

Il est possible d'utiliser l'annotation @JsonbNillable sur une classe ou un package pour demander la sérialisation des valeurs des propriétés de la ou des classes concernées.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
 
import javax.json.bind.annotation.JsonbNillable;
 
@JsonbNillable
public class Personne {
 
  // ...

}
Résultat :
{"adulte":true,"dateNaissance":"1985-08-11","nom":null,"prenom":null,"taille":175}

Il est possible d'utiliser l'annotation @JsonbProperty avec son attribut nillable=true sur un champ pour demander de sérialiser sa valeur si elle est null.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
 
import javax.json.bind.annotation.JsonbProperty;
 
public class Personne {
 
  private String    nom;
  @JsonbProperty(nillable = true)
  private String    prenom;
 
  // ...

}
Résultat :
{"adulte":true,"dateNaissance":"1985-08-11","prenom":null,"taille":175}

Il est possible de modifier globalement la configuration pour que toutes les valeurs null soient sérialisées en invoquant la méthode withNullValues(true) de l'instance de JsonbConfig utilisée pour obtenir l'instance du moteur.

Exemple ( code Java 8 ) :
    JsonbConfig config = new JsonbConfig().withNullValues(true);
    Jsonb jsonb = JsonbBuilder.create(config);
    String result = jsonb.toJson(pers);
    System.out.println(result);
Résultat :
{"adulte":true,"dateNaissance":"1985-08-11","nom":null,"prenom":null,"taille":175}

 

57.5.7. La personnalisation de la création d'instanciation

Par défaut, les classes utilisées lors de la désérialisation doivent avoir un constructeur par défaut qui sera invoqué par le moteur JSON-B pour créer une instance. Cette contrainte est parfois trop contraignante : c'est par exemple le cas pour un objet immuable.

L'annotation @javax.json.bind.annotation.JsonbCreator peut être utilisée sur un constructeur ou une fabrique statique pour indiquer au moteur JSON comment créer une instance.

L'annotation @JsonbCreator ne possède aucun attribut et ne peut être utilisée que sur une seule méthode statique qui est une fabrique ou un seul constructeur avec paramètre(s) dans la classe. Si ce n'est pas le cas alors une exception de type JsonbException est levée.

Pour assurer la bonne liaison entre les données lues du document JSON et les paramètres du constructeur ou de la fabrique, il faut annoter chaque paramètre avec l'annotation @JsonbProperty() en lui précisant comme valeur le nom de l'attribut. Le nom de l'attribut précisé doit exister dans le document JSON sinon une exception de type JsonbException est levée.

Si l'annotation @JsonbProperty n'est pas utilisée sur les paramètres, le moteur sa tenter de faire une correspondance avec le nom du paramètre, sous réserve que celui-ci soit accessible.

Il est possible d'utiliser l'annotation @JsonbCreator sur un constructeur.

Exemple ( code Java 8 ) :
import javax.json.bind.annotation.JsonbCreator;
import javax.json.bind.annotation.JsonbProperty;
 
public class Produit {
  private long   id;
  private String libelle;
  private String reference;
 
  @JsonbCreator
  public Produit(@JsonbProperty("id") long id,
      @JsonbProperty("libelle") String lib,
      @JsonbProperty("reference") String ref) {
    this.id = id;
    libelle = lib;
    reference = ref;
    System.out.println("Invocation constructeur Produit");
  }
 
  public long getId() {
    return id;
  }
 
  public String getLibelle() {
    return libelle;
  }
 
  public String getReference() {
    return reference;
  }
}
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    JsonbConfig config = new JsonbConfig().withStrictIJSON(true);
    Jsonb jsonb = JsonbBuilder.create(config);
 
    Produit produit = new Produit(1, "test", "ref1");
    String result = jsonb.toJson(produit);
    System.out.println(result);
 
    Produit prod = jsonb.fromJson(result, Produit.class);
    System.out.println("id       =" + prod.getId());
    System.out.println("libelle  =" + prod.getLibelle());
    System.out.println("reference=" + prod.getReference());
  }
}
Résultat :
Invocation constructeur Produit
{"id":1,"libelle":"test","reference":"ref1"}
Invocation constructeur Produit
id       =1
libelle  =test
reference=ref1

Il est possible d'utiliser l'annotation @JsonbCreator sur une méthode statique qui est une fabrique pour obtenir une instance : elle doit donc renvoyer une instance du type de la classe sinon une exception de type JsonbException est levée.

Exemple ( code Java 8 ) :
import javax.json.bind.annotation.JsonbCreator;
import javax.json.bind.annotation.JsonbProperty;
 
public class Produit {
  private long   id;
  private String libelle;
  private String reference;
 
  @JsonbCreator
  public static Produit creerInstance(@JsonbProperty("id") long id,
      @JsonbProperty("libelle") String lib,
      @JsonbProperty("reference") String ref) {
    Produit result = new Produit();
    result.id = id;
    result.libelle = lib;
    result.reference = ref;
    System.out.println("Invocation fabrique Produit");
    return result;
  }
 
  public long getId() {
    return id;
  }
 
  public String getLibelle() {
    return libelle;
  }
 
  public String getReference() {
    return reference;
  }
}
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    JsonbConfig config = new JsonbConfig().withStrictIJSON(true);
    Jsonb jsonb = JsonbBuilder.create(config);
 
    Produit produit = Produit.creerInstance(1, "test", "ref1");
    String result = jsonb.toJson(produit);
    System.out.println(result);
 
    Produit prod = jsonb.fromJson(result, Produit.class);
    System.out.println("id       =" + prod.getId());
    System.out.println("libelle  =" + prod.getLibelle());
    System.out.println("reference=" + prod.getReference());
  }
}
Résultat :
Invocation fabrique Produit
{"id":1,"libelle":"test","reference":"ref1"}
Invocation fabrique Produit
id       =1
libelle  =test
reference=ref1

 

57.5.8. Le format des dates et des nombres

Par défaut, JSON-B utilise les formats ISO pour sérialiser et désérialiser des données temporelles et numériques.

Exemple ( code Java 8 ) :
import java.math.BigDecimal;
import java.util.Date;
 
public class ResultatEpreuve {
  public long       idEpreuve;
  public long       idCandidat;
  public Date       date;
  public BigDecimal note;
}
Exemple ( code Java 8 ) :
import java.math.BigDecimal;
import java.util.Date;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    JsonbConfig config = (new JsonbConfig()).withFormatting(true);
    Jsonb jsonb = JsonbBuilder.create(config);
    ResultatEpreuve re = new ResultatEpreuve();
    re.date = new Date();
    re.idCandidat = 123;
    re.idEpreuve = 456;
    re.note = new BigDecimal("15.5");
    String result = jsonb.toJson(re);
    System.out.println(result); 
  }
}
Résultat :
{
    "date": "2017-08-31T07:53:37.753Z[UTC]",
    "idCandidat": 123,
    "idEpreuve": 456,
    "note": 15.5
}

Pour utiliser un format particulier pour un champ, il est possible d'utiliser les annotations @JsonbDateFormat et @JsonbNumberFormat.

Exemple ( code Java 8 ) :
import java.math.BigDecimal;
import java.util.Date;
 
import javax.json.bind.annotation.JsonbDateFormat;
import javax.json.bind.annotation.JsonbNumberFormat;
 
public class ResultatEpreuve {
  public long       idEpreuve;
  public long       idCandidat;
  @JsonbDateFormat("dd/MMM/yyyy")
  public Date       date;
  @JsonbNumberFormat("#0.00")
  public BigDecimal note;
}
Résultat :
{
    "date": "31/Aug/2017",
    "idCandidat": 123,
    "idEpreuve": 456,
    "note": "15,50"
}

Les annotations @JsonbDateFormat et @JsonbNumberFormat peuvent s'utiliser sur un champ, un getter/setter, un type, un paramètre ou un package.

L'annotation @JsonbDateFormat possède deux attributs optionnels :

Attribut

Rôle

String locale

La Locale à utiliser

String value

Le motif de formatage à utiliser exprimé avec le format supporté par la classe DateTimeFormatter


Exemple ( code Java 8 ) :
  // ...

  @JsonbDateFormat(value = "dd/MMM/yyyy", locale = "FR")
  public Date       date;
  // ...

Résultat :
{
    "date": "31/août/2017",
    "idCandidat": 123,
    "idEpreuve": 456,
    "note": "15,50"
}

L'annotation @JsonbNumberFormat possède deux attributs optionnels :

Attribut

Rôle

String locale

La Locale à utiliser

String value

Le motif de formatage à utiliser en utilisant le format supporté par la classe DecimalFormat


Il est aussi possible de configurer globalement le format des dates utilisant une instance de type JsonbConfig qui propose deux méthodes :

Méthode

Rôle

JsonbConfig withDateFormat(String dateFormat, Locale locale)

Préciser le format de date et la Locale à utiliser

JsonbConfig withLocale(Locale locale)

Préciser la Locale à utiliser


Exemple ( code Java 8 ) :
import java.math.BigDecimal;
import java.util.Date;
import java.util.Locale;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    JsonbConfig config = (new JsonbConfig()).withFormatting(true)
        .withDateFormat("dd/MMM/yyyy", Locale.ITALIAN);
    Jsonb jsonb = JsonbBuilder.create(config);
    ResultatEpreuve re = new ResultatEpreuve();
    re.date = new Date();
    re.idCandidat = 123;
    re.idEpreuve = 456;
    re.note = new BigDecimal("15.5");
    String result = jsonb.toJson(re);
    System.out.println(result);
  }
}
Résultat :
{
    "date": "31/ago/2017",
    "idCandidat": 123,
    "idEpreuve": 456,
    "note": 15.5
}

 

57.5.9. Binary Data Encoding

JSON-B supporte les données binaires. Les trois types d'encodage supportés sont définis sous la forme de constantes dans la classe BinaryDataStrategy:

  • BYTE : avec cette stratégie, les données sont encodées comme un tableau d'octets. C'est la stratégie par défaut
  • BASE_64 : avec cette stratégie, les données sont encodées en Base64 selon les RFC 4648 et RFC 2045
  • BASE_64_URL : avec cette stratégie, les données sont encodées en utilisant "URL and Filename safe Base64 Alphabet" défini dans la Table 2 de la RFC 4648

Pour définir la stratégie globale de binding des données binaires, il faut passer une de ces constantes à la méthode withBinaryDataStrategy() de la classe JsonbConfig.

Exemple ( code Java 8 ) :
import java.util.Random;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import javax.json.bind.config.BinaryDataStrategy;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    byte[] donnees = new byte[10];
    new Random().nextBytes(donnees);
 
    System.out.println("BYTE        " + serialiserBinaire(donnees,
        BinaryDataStrategy.BYTE));
    System.out.println("BASE_64     " + serialiserBinaire(donnees,
        BinaryDataStrategy.BASE_64));
    System.out.println("BASE_64_URL " + serialiserBinaire(donnees,
        BinaryDataStrategy.BASE_64_URL));
  }
 
  private static String serialiserBinaire(byte[] donnees, String strategie) {
    JsonbConfig config = new JsonbConfig().withBinaryDataStrategy(strategie);
    Jsonb jsonb = JsonbBuilder.create(config);
    String result = jsonb.toJson(donnees);
    return result;
  }
}
Résultat :
BYTE        [108,47,87,-83,77,-1,3,-90,-111,95]
BASE_64     "bC9XrU3/A6aRXw=="
BASE_64_URL "bC9XrU3_A6aRXw=="

 

57.5.10. Strict I-JSON support

Internet JSON ou I-JSON est une spécification pour définir un profile sur l'utilisation de JSON afin de maximiser l'interopérabilité de l'exploitation des documents par des applications.

JSON-B propose un support de I-JSON par défaut à l'exception de trois recommandations :

  • JSON-B ne limite pas la sérialisation en un document JSON dont le niveau supérieur soit un objet ou un tableau
  • JSON-B ne sérialise pas les données binaires avec l'encodage base 64 url par défaut
  • JSON Binding ne met pas en oeuvre pas les restrictions supplémentaires recommandées par I-JSON sur les dates, les heures ou une durée

Pour activer le support complet, il faut utiliser la méthode withStrictIJSON() avec la valeur true en paramètre de l'instance de type JsonbConfig utilisée pour configurer le moteur.

Exemple ( code Java 8 ) :
    // ...

    JsonbConfig config = new JsonbConfig().withStrictIJSON(true);
    Jsonb jsonb = JsonbBuilder.create(config);
    // ...

 

57.5.11. Les adaptateurs

Parfois, il n'est pas toujours possible d'utiliser des annotations pour personnaliser le binding, par exemple parce que le code source n'est pas disponible ou parce que les annotations proposées ne permettent pas de répondre aux besoins. Dans ces cas, il est possible d'utiliser un adaptateur pour tenter de répondre aux besoins.

Les adaptateurs de JSON-B fonctionnent de manière similaire à ceux proposés par JAX-B.

Un adaptateur est une classe qui implémente l'interface javax.json.bind.adapter.JsonbAdapter. L'interface JsonbAdapter<Original, Adapted> possède deux types génériques :

  • Original : le type qui n'est pas pris en charge par JSON-B
  • Adapted : le type que JSON-B sait prendre en compte

Elle définit deux méthodes :

Méthode

Rôle

Original adaptFromJson(Adapted obj)

Cette méthode est invoquée uniquement lors de la désérialisation

Adapted adaptToJson(Original obj)

Cette méthode est invoquée uniquement lors de la sérialisation


L'implémentation doit contenir les traitements pour créer une instance de type Adapted à partir d'une instance de type Original et vice versa. Cela permettra au moteur JSON-B de sérialiser/désérialiser le type Adapted à la place du type Original.

Exemple ( code Java 8 ) :
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.bind.adapter.JsonbAdapter;
 
public class PersonneAdapter implements JsonbAdapter<Personne, JsonObject> {
 
  @Override
  public JsonObject adaptToJson(Personne pers) throws Exception {
    return Json.createObjectBuilder()
        .add("n", pers.getNom())
        .add("p", pers.getPrenom())
        .build();
  }
 
  @Override
  public Personne adaptFromJson(JsonObject obj) throws Exception {
    Personne p = new Personne();
    p.setNom(obj.getString("n"));
    p.setPrenom(obj.getString("p"));
    return p;
  }
}

Lors d'une sérialisation d'un objet de type Original, le moteur JSON-B invoque la méthode adaptToJson() de l'adaptateur pour obtenir une instance de type Adapted et la sérialiser.

Lors d'une désérialisation d'un objet de type Adapted, le moteur JSON-B invoque la méthode adaptFromJson() de l'adaptateur pour obtenir une instance de type Original et la retourner.

Il y a deux manières d'enregistrer un adaptateur pour qu'il soit pris en compte par le moteur :

  • Utiliser la méthode withAdapters() de la classe JsonbConfig
  • Utiliser l'annotation @JsonbTypeAdapter

Il est possible d'enregistrer globalement un adaptateur en utilisant la méthode withAdapters() de la classe JsonbConfig en lui passant une nouvelle instance de l'adaptateur en paramètre. L'adaptateur sera alors utilisé à chaque sérialisation/désérialisation d'un objet de type Original.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
import java.time.Month;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    JsonbConfig config = new JsonbConfig().withAdapters(new PersonneAdapter());
    Jsonb jsonb = JsonbBuilder.create(config);
    Personne pers = new Personne();
    pers.setNom("nom");
    pers.setPrenom("prenom");
    pers.setTaille(175);
    pers.setAdulte(true);
    pers.setDateNaissance(LocalDate.of(1985, Month.AUGUST, 11));
 
    String result = jsonb.toJson(pers);
    System.out.println("result=" + result);
 
    pers = jsonb.fromJson(result, Personne.class);
    System.out.println("nom=" + pers.getNom());
    System.out.println("dateNaissance=" + pers.getDateNaissance());
  }
}

Pour utiliser l'adaptateur uniquement pour un champ, il est possible de l'annoter avec @JsonbTypeAdapter en lui précisant comme attribut le type de l'adaptateur.

Exemple ( code Java 8 ) :
import javax.json.bind.annotation.JsonbTypeAdapter;
 
public class MonBean {
  public String   id = "1";
  @JsonbTypeAdapter(PersonneAdapter.class)
  public Personne personne;
}
Exemple ( code Java 8 ) :
import java.time.LocalDate;
import java.time.Month;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
    Jsonb jsonb = JsonbBuilder.create();
    Personne pers = new Personne();
    pers.setNom("nom");
    pers.setPrenom("prenom");
    pers.setTaille(175);
    pers.setAdulte(true);
    pers.setDateNaissance(LocalDate.of(1985, Month.AUGUST, 11));
 
    MonBean monBean = new MonBean();
    monBean.personne = pers;
 
    String result = jsonb.toJson(monBean);
    System.out.println("result=" + result);
 
    monBean = jsonb.fromJson(result, MonBean.class);
    System.out.println("nom=" + monBean.personne.getNom());
    System.out.println("dateNaissance=" + monBean.personne.getDateNaissance());
  }
}
Résultat :
{
    "date": "31/ago/2017",
    "idCandidat": 123,
    "idEpreuve": 456,
    "note": 15.5
}

 

57.5.12. Les Serializers/Deserializers

Les Serializers/Deserializers permettent d'avoir un contrôle de bas niveau sur les opérations de sérialisation/désérialisation grâce à une utilisation de JSON-P. Ils sont par exemple utiles lorsque les adaptateurs ne suffisent pas.

Un Serializer est une classe qui implémente l'interface javax.json.bind.serializers.JsonbSerializer.

Un Deserializer est une classe qui implémente l'interface javax.json.bind.serializers.JsonbDeserializer.

Il y a deux possibilités pour enregistrer un Serializer ou Deserializer :

  • De manière globale en utilisant les méthodes withSerializer() et/ou withDeserializer() de la classe JsonbConfig
  • Utiliser l'annotation @JsonbSerializer et/ou @JsonbDeserializer sur un type

L'interface JsonbSerializer<T> permet de personnaliser la sérialisation d'un objet de type T. Elle permet un contrôle très précis de la sérialisation en utilisant un objet de type JsonGenerator de l'API de type Stream de JSON-P.

L'interface JsonbSerializer<T> définit une seule méthode :

Méthode

Rôle

void serialize(T obj, javax.json.stream.JsonGenerator generator, SerializationContext ctx)

Sérialiser l'objet passé en paramètre en utilisant l'API Stream de JSON-P


L'implémentation de la méthode serialize() doit utiliser l'objet de type JsonGenerator pour créer le document JSON à partir de l'objet passé en paramètre.

Exemple ( code Java 8 ) :
import javax.json.bind.serializer.JsonbSerializer;
import javax.json.bind.serializer.SerializationContext;
import javax.json.stream.JsonGenerator;
 
public class PersonneSerializer implements JsonbSerializer<Personne> {
  @Override
  public void serialize(Personne personne, JsonGenerator generator,
      SerializationContext ctx) {
    generator.writeStartObject();
    generator.write("nom_s", personne.getNom());
    generator.write("prenom_s", personne.getPrenom());
    generator.writeEnd();
  }
}

Dans les traitements de l'implémentation de la méthode serialize(), il est possible d'utiliser l'instance de type SerializationContext pour sérialiser un objet.

L'interface SerialisationContext propose deux méthodes pour faciliter la sérialisation d'un objet :

Méthode

Rôle

<T> void serialize(String key, T object, javax.json.stream.JsonGenerator generator)

Sérialiser l'objet passé en paramètre en utilisant l'instance de type JsonGenerator sous la forme d'un attribut dont le nom est passé en paramètre

<T> void serialize(T object, javax.json.stream.JsonGenerator generator)

Sérialiser l'objet passé en paramètre en utilisant l'instance de type JsonGenerator sous la forme d'un tableau


Pour utiliser le Serializer de manière globale, il faut en passer une instance en paramètre de la méthode withSerializer() de la classe JsonbConfig.

Exemple ( code Java 8 ) :
import java.time.LocalDate;
import java.time.Month;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    JsonbConfig config = new JsonbConfig()
        .withSerializers(new PersonneSerializer());
    Jsonb jsonb = JsonbBuilder.create(config);
 
    Personne pers = new Personne();
    pers.setNom("nom");
    pers.setPrenom("prenom");
    pers.setTaille(175);
    pers.setAdulte(true);
    pers.setDateNaissance(LocalDate.of(1985, Month.AUGUST, 11));
    String result = jsonb.toJson(pers);
    System.out.println(result);
  }
}
Résultat :
{"nom_s":"nom","prenom_s":"prenom"}

Il est aussi possible d'utiliser l'annotation @JsonbTypeSerializer en lui passant comme valeur la classe de l'implémentation du JsonbSerializer.

Exemple ( code Java 8 ) :
import javax.json.bind.annotation.JsonbTypeSerializer;
 
public class MonBean {
  public String   id = "1";
  @JsonbTypeSerializer(PersonneSerializer.class)
  public Personne personne;
}
Exemple ( code Java 8 ) :
import java.time.LocalDate;
import java.time.Month;
 
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
  
    Jsonb jsonb = JsonbBuilder.create();
 
    MonBean monBean = new MonBean();
    Personne pers = new Personne();
    pers.setNom("nom");
    pers.setPrenom("prenom");
    pers.setTaille(175);
    pers.setAdulte(true);
    pers.setDateNaissance(LocalDate.of(1985, Month.AUGUST, 11));
 
    monBean.id = "1";
    monBean.personne = pers;
 
    String result = jsonb.toJson(monBean);
    System.out.println(result);
  }
}
Résultat :
{"id":"1","personne":{"nom_s":"nom","prenom_s":"prenom"}}

L'interface JsonbDeserializer<T> permet de personnaliser la désérialisation d'un objet de type T. Elle permet un contrôle très précis de la désérialisation en utilisant un objet de type JsonParser de l'API de type Stream de JSON-P.

L'interface JsonbDeserializer<T> définit une seule méthode :

Méthode

Rôle

T deserialize(javax.json.stream.JsonParser parser, DeserializationContext ctx, Type rtType)

Désérialiser l'objet passé en paramètre en utilisant l'API Stream de JSON-P


L'implémentation de la méthode deserialize() doit utiliser l'objet de type JsonParser pour extraire les données du document JSON et créer l'instance à retourner.

Exemple ( code Java 8 ) :
import java.lang.reflect.Type;
 
import javax.json.bind.serializer.DeserializationContext;
import javax.json.bind.serializer.JsonbDeserializer;
import javax.json.stream.JsonParser;
import javax.json.stream.JsonParser.Event;
 
public class PersonneDeserializer implements JsonbDeserializer<Personne> {
 
  @Override
  public Personne deserialize(JsonParser parser, DeserializationContext ctx,
      Type rtType) {
    Personne result = new Personne();
 
    while (parser.hasNext()) {
      Event event = parser.next();
      if (event == JsonParser.Event.KEY_NAME) {
        String nomAttr = parser.getString();
        parser.next();
        if (nomAttr.equals("nom_s")) {
          result.setNom(parser.getString());
 
        } else if (nomAttr.equals("prenom_s")) {
          result.setPrenom(parser.getString());
        }
      }
    } 
    return result;
  }
}

Dans les traitements de l'implémentation de la méthode deserialize(), il est possible d'utiliser l'instance de type DeserializationContext pour désérialiser un objet.

L'interface DeserializationContext propose deux méthodes pour faciliter la désérialisation d'un objet :

Méthode

Rôle

<T> T deserialize(Class<T> clazz, javax.json.stream.JsonParser parser)

Désérialiser l'objet passé en paramètre en utilisant l'instance de type JsonParser

<T> T deserialize(Type type, javax.json.stream.JsonParser parser)

Désérialiser l'objet passé en paramètre en utilisant l'instance de type JsonParser


Pour utiliser le Deserializer de manière globale, il faut en passer une instance en paramètre de la méthode withDeserializer() de la classe JsonbConfig.

Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    JsonbConfig config = new JsonbConfig()
        .withSerializers(new PersonneSerializer())
        .withDeserializers(new PersonneDeserializer());
    Jsonb jsonb = JsonbBuilder.create(config);
    Personne pers = jsonb.fromJson("{\"nom_s\":\"nom1\",\"prenom_s\":\"prenom1\"}",
        Personne.class);
    System.out.println("nom   =" + pers.getNom());
    System.out.println("prenom=" + pers.getPrenom());
  }
}
Résultat :
nom   =nom1
prenom=prenom1

Il est aussi possible d'utiliser l'annotation @JsonbTypeDeserializer en lui passant comme valeur la classe de l'implémentation du JsonbDeserializer.

Exemple ( code Java 8 ) :
import javax.json.bind.annotation.JsonbTypeDeserializer;
import javax.json.bind.annotation.JsonbTypeSerializer;
 
public class MonBean {
  public String   id = "1";
  @JsonbTypeSerializer(PersonneSerializer.class)
  @JsonbTypeDeserializer(PersonneDeserializer.class)
  public Personne personne;
}
Exemple ( code Java 8 ) :
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
 
public class TestJsonB {
 
  public static void main(String[] args) {
 
    Jsonb jsonb = JsonbBuilder.create();
    MonBean monBean = jsonb.fromJson(
        "{\"id\":\"1\",\"personne\":{\"nom_s\":\"nom\",\"prenom_s\":\"prenom\"}}",
        MonBean.class);
    System.out.println("nom   =" + monBean.personne.getNom());
    System.out.println("prenom=" + monBean.personne.getPrenom());
  }
}
Résultat :
nom   =nom
prenom=prenom

 

 


[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

78 commentaires Donner une note à l´article (5)

 

Copyright (C) 1999-2022 Jean-Michel DOUDOUX. Vous pouvez copier, redistribuer et/ou modifier ce document selon les termes de la Licence de Documentation Libre GNU, Version 1.1 ou toute autre version ultérieure publiée par la Free Software Foundation; les Sections Invariantes étant constitués du chapitre Préambule, aucun Texte de Première de Couverture, et aucun Texte de Quatrième de Couverture. Une copie de la licence est incluse dans la section GNU FreeDocumentation Licence. La version la plus récente de cette licence est disponible à l'adresse : GNU Free Documentation Licence.