{Sérialisation / desérialisation JSON}

A. JSONProvider [jettison]
B. JSONProvider [jackson]
C. Possibilité de modifier certains attributs de sérialisation dans le code Adélia d'une classe (V13 PTF7 Fix01)


A.JSONProvider [jettison]


Il est possible de paramétrer la sérialisation JSON des services REST à l'aide du fichier de configuration Spring "/WEB-INF/beans.xml" en déclarant un bean Spring faisant référence à la classe org.apache.cxf.jaxrs.provider.json.JSONProvider (provider fourni et utilisé par défaut par le framework CXF, ce provider s'appuie sur la librairie Jettison pour la sérialisation/desérialisation des classes java).

Détail des propriétés du bean Spring :

  1. Par défaut, jettison sérialise les collections (tableaux) d'un seul élément non pas sous la forme d'un tableau JSON d'un seul élément mais sous la forme d'un élément seul.
    Les propriétés serializeAsArray et arraysKeys permettent de sérialiser une collection d'un seul élément sous forme d'un tableau. Il est cependant nécessaire de préciser explicitement les noms des collections concernées.

    Exemple : L'exemple ci-dessous présente la sérialisation d'une classe nommée "STRUCTURE" contenant 2 collections, l'une nommée "dimensions_lst" et l'autre nommée "measures_lst".
    La collection "dimensions_lst" contient 2 éléments et est sérialisée par défaut comme un tableau.
    La collection "measures_lst" ne contient qu'un seul élément et est sérialisée comme un simple élément.

    {
        "STRUCTURE": {
            "dimensions_lst": [{
                "dimensionId": 1,
                "description": "d1"
            }, {
                "dimensionId": 2,
                "description": "d2"
            }],
            "measures_lst": {
                "measureId": 1,
                "description": "d1"
            }
        }
    }


    Paramétrage du bean JSONProvider pour changer la sérialisation de la collection nommée "measures_lst"

     <bean class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
          <property name="serializeAsArray" value="true"/>
          <property name="arrayKeys">
             <list>
                <value>measures_lst</value>
             </list>
          </property>
    </bean>


    L'application de la configuration présentée ci-dessus permet de sérialiser la collection "measures_lst" sous la forme d'un tableau JSON (utilisatiion des 'brackets' []).

    {
        "STRUCTURE": {
            "dimensions_lst": [{
                "dimensionId": 1,
                "description": "d1"
            }, {
                "dimensionId": 2,
                "description": "d2"
            }],
            "measures_lst": [{
                "measureId": 1,
                "description": "d1"
            }]
        }
    }



  2. La propriété dropRootElement permet de supprimer l'élément racine.
    Par défaut, l'élément racine (nom de la classe "STRUCTURE") est présent dans la sérialisation JSON.

    Paramétrage du bean JSONProvider pour supprimer l'élément racine de la sérialisation de la classe.

    <bean class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
          <property name="dropRootElement" value="true"/>      
    </bean>


    L'application de la configuration présentée ci-dessus permet de supprimer l'élément racine "STRUCTURE" de la sérialisation.

    {
        "dimensions_lst": [{
            "dimensionId": 1,
            "description": "d1"
        }, {
            "dimensionId": 2,
            "description": "d2"
        }],
        "measures_lst": [{
            "measureId": 1,
            "description": "d1"
        }]
    }

     

    Dans le même ordre d'idée, mais dans le cadre de la desérialisation JSON en entrée du service, il est possible de d'accepter des objets JSON sans élément racine, à l'aide de la propriété :

    <property name="supportUnwrapped" value="true" />



  3. Convention badgerfish

    <property name="convention" value="badgerfish" />


    Exemple:

    {
        "STRUCTURE": {
            "dimensions_lst": [{
                "dimensionId": {
                    "$": "1"
                },
                "description": {
                    "$": "d1"
                }
            }, {
                "dimensionId": {
                    "$": "2"
                },
                "description": {
                    "$": "d2"
                }
            }],
            "measures_lst": {
                "measureId": {
                    "$": "1"
                },
                "description": {
                    "$": "d1"
                }
            }
        }
    }
  4. Autres propriétés
    - ignoreNamespaces
    - writeXsiType / readXsiType
    - ignoreMixedContent
    - ignoreEmptyArrayValues
    - writeNullAsString

 

Ce paramétrage peut-être externalisé grâce à une ressource JNDI désignant un fichier de propriétés : Services web REST - Externalisation de la configuration

 

B. JSONProvider [jackson : JacksonJaxbJsonProvider]

Il est possible de changer le JSONProvider par défaut par un provider s'appuyant non plus sur la librairie Jettison mais sur la librairie Jackson.
Le comportement par défaut de la librairie Jackson diffère de celle de Jettison en plusieurs points.

  • droptRootElement est actif par défaut.
  • les collections contenant un seul élément sont sérialisées par défaut en un 1 tableau JSON et non comme un élément seul
  • le type IMAGE n'est pas sérialisé de façon identique avant la PTF 07 FIX 01. A partir de la PTF 07 Fix 01 Jackson et Jettison sérialisent le type IMAGE de la même façon.

 

Pour utiliser un JSONProvider s'appuyant sur la librairie Jackson et sur les annotations JAXB et Jackson générées dans les classes, il faut modifier le fichier WEB-INF/beans.xml de la sorte :

<bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/> 

et déployer les librairies jackson-jaxrs-1.9.x.jar, jackson-core-asl-1.9.x.jar,  jackson-xc-1.9.x.jar et jackson-mapper-asl-1.9.x.jar. Ces bibliothèques sont fournies directement à partir de la V13 PTF08 FIX01.
Ces librairies peuvent éventuellement être tirées par la dépendance Maven suivante :

<dependency>
  <groupId>org.codehaus.jackson</groupId>
  <artifactId>jackson-jaxrs</artifactId>
  <version>1.9.x</version>
</dependency>

<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-xc</artifactId>
    <version>1.9.x</version>
</dependency>

(V14)

A partir de cette version, la librairie Jackson devient la librairie utilisée par défaut. Le fichier beans.xml de référence a été modifié afin que les sites web Adélia nouvellement créés utilisent la librairie Jackson en standard.

(V14.2.0)

La librairie Jackson utilisée par défaut est désormais com.fasterxml.jackson.jaxrs.JacksonJaxbJsonProvider. Cette librairie assure d'une part une sérialisation optimale des attributs de classe de type table dynamique (application du TRIM) et d'autre part une homogénéité (sérialisation et desérialisation) avec l'ordre CONV_DONNEES.

Pour utiliser le nouveau JacksonJaxbJsonProvider il faut déclarer - dans le fichier beans.xml - les 2 entrées suivantes :

<cxf:bus> 
   <cxf:properties>
      <entry key="skip.default.json.provider.registration" value="true"/>
   </cxf:properties>
</cxf:bus>


<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider"/>

(V14.3.2) Modification du httpStatus par défaut lié à une erreur de parsage en entrée d'un document JSON


Par défaut, une erreur de parsage d'un document JSON retourne un httpStatus égal à 500 (internal server error).
Il est possible de modifier ce httpStatus en déclararant des 'ExceptionMapper' dans le fichier beans.xml.
Pour ce faire 2 'ExceptionMapper' disctincts sont disponibles :

  • JsonUnrecognizedPropertyExceptionMapper : cas d'un attribut non reconnu dans le modèle.
  • JsonParseExceptionMapper : cas d'un document json non valide.

    Pré-requis : utilisation du provider Json "com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider". Cf. Paragraphe précédent.

/* La simple déclaration du bean JsonUnrecognizedPropertyExceptionMapper permet de
   fixer un httpStatus à 400 (bad request) dans le cas d'un attribut json non reconnu.
   Le format par défaut de la réponse est du 'text/plain'.*/
<bean class="com.hardis.adelia.webservice.exception.JsonUnrecognizedPropertyExceptionMapper"/>

/* Possibilité de choisir la valeur du httpStatus (parmi les valeurs valides) et du 
   format de la réponse à l'aide, respectivement, des propriétés 'httpStatus' et 'mediaType'.
   Exemple : 
   - httpStatus retourné = 417 
   - format de la réponse : application/json */
<bean class="com.hardis.adelia.webservice.exception.JsonUnrecognizedPropertyExceptionMapper">
  <property name="httpStatus" value="417" />
  <property name="mediaType" value="application/json" />
</bean>
   

/* La simple déclaration du bean JsonParseExceptionMapper permet de
   fixer un httpStatus à 400 (bad request) dans le cas d'une erreur de parsage
   du document json.
   Le format par défaut de la réponse est du 'text/plain'.*/
<bean class="com.hardis.adelia.webservice.exception.JsonParseExceptionMapper"/>

/* Possibilité de choisir la valeur du httpStatus (parmi les valeurs valides) et du
   format de la réponse à l'aide, respectivement, des propriétés 'httpStatus' et 'mediaType'.
   Exemple : 
   - httpStatus retourné = 417 
   - format de la réponse : application/json */
<bean class="com.hardis.adelia.webservice.exception.JsonParseExceptionMapper">
  <property name="httpStatus" value="417" />
  <property name="mediaType" value="application/json" />
</bean>


(V14.4.0) Paramétrage de la sérialisation/desérialisation JSON [com.fasterxml.jackson.jaxrs.JacksonJaxbJsonProvider]

Possibilité de paramétrer la sérialisation/desérialisation JSON basées sur la librairie Jackson pour le provider com.fasterxml.jackson.jaxrs.JacksonJaxbJsonProvider

Il faut déclarer un bean de type com.hardis.adelia.webservice.CustomObjectMapper avec les propriétés à fixer.
Il faut ensuite passer ce bean en paramètre du constructeur du JacksonJaxbJsonProvider.

Remarque : d'une part les noms des propriétés suivent une notation de style "camel case". D'autre part,

  • les propriétés 'SerializationFeature'  sont préfixées par Ser.
  • les propriétés 'DeserializationFeature'  sont préfixées par Deser.

Exemple :  DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES devient DeserFailOnUnknownProperties


L'exemple ci-dessous permet d'éviter les erreurs de desérialisation liées aux propriétés Json non reconnues. [Propriété DeserFailOnUnknownProperties]

Beans.xml
<bean id="customJacksonMapper" class="com.hardis.adelia.webservice.CustomObjectMapper" init-method="init">
	<property name="DeserFailOnUnknownProperties" value="false" />
</bean>

<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider">
   <constructor-arg index="0" ref="customJacksonMapper" />
   <constructor-arg index="1"><null /></constructor-arg>
</bean>

(V14.6.0) Ajout de la propriété de sérialisation SerInclusion

Cette propriété joue sur l'inclusion des éléments à sérialiser en fonction des valeurs suivantes :

  • Always (default) : Value that indicates that property is to be always included, independent of value of the property.
  • NonAbsent : Value that indicates that properties are included unless their value is: null "absent" value of a referential type (like Java 8 `Optional`, or {link java.utl.concurrent.atomic.AtomicReference}); that is, something that would not deference to a non-null value.
  • NonDefault : Meaning of this setting depends on context: whether annotation is specified for POJO type (class), or not.
  • NonEmpty : Value that indicates that only properties with null value, or what is considered empty, are not to be included.
  • NonNull : Value that indicates that only properties with non-null values are to be included.
  • UseDefaults : Pseudo-value used to indicate that the higher-level defaults make sense, to avoid overriding inclusion value.

    La valeur NonNull permet notamment de revenir au comportement par défaut de l'ancienne implémentation org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider

Exemple : 

Beans.xml
<bean id="customJacksonMapper" class="com.hardis.adelia.webservice.CustomObjectMapper" init-method="init">
	<property name="SerInclusion" value="NonNull" />
</bean>

<bean class="com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider">
   <constructor-arg index="0" ref="customJacksonMapper" />
   <constructor-arg index="1"><null /></constructor-arg>
</bean>

C. Possibilité de modifier certains attributs de sérialisation dans le code Adélia d'une classe

Des directives de sérialisation sont disponibles dans le source Adélia d'une classe.

(V13 PTF7 Fix01)

*SER_NOM('nom') : définition d'un nom externe pour le membre d'une classe.
*SER_FACULTATIF[('ValeurDef')] : Par défaut, un membre est exposé comme obligatoire. La directive qualifie le membre comme optionnel. La valeur par défaut (si précisée) est utilisée pour la désérialisation.
*SER_MODE(*VERBEUX) : S'applique à un membre de type IMAGE. La sérialisation (à partir du Fix01) du type IMAGE est réduit à une simple chaine encodée en base64, le mode *VERBEUX restitue la sérialisation à ce qu'elle était avant le fix01 de la PTF07.

Exemple :

*ATTRIBUTS
{
	NUM_BIN_2 Name    *SER_NOM('Nom');
	ALPHA(50) Address *SER_NOM('Adresse') *SER_FACULTATIF('75000 Paris');
	IMAGE     Picture *SER_MODE(*VERBEUX);
}

 

(V13 PTF9)

*SER_NOM('nom') : définition d'un nom externe pour la classe.
*SER_DESC('description') : définition d'une description pour la classe ou un membre de la classe. Dans l'optique d'une présentation via swagger.
*SER_EXEMPLE('ValeurExemple') : définition d'une valeur exemple pour un membre de la classe. Dans l'optique d'une présentation via swagger.

Exemple :

*ATTRIBUTS *SER_NOM('Personne') *SER_DESC('Définition d''une personne')
{
	 ALPHA(32) Name    *SER_NOM('Nom')     *SER_EXEMPLE('DUPONT') *SER_DESC('Nom de la personne');
	 ALPHA(50) Address *SER_NOM('Adresse') *SER_EXEMPLE('Rue des Martyrs - 75000 Paris') *SER_DESC('Adresse postale de la personne');
	 IMAGE     Picture *SER_MODE(*VERBEUX) *SER_DESC('Photo d''identité');
}

 

Exemple de présentation d'un modèle (classe) dans swagger-ui

 

 

 

 




  • Aucune étiquette