Exemples d’annotation de Jackson

Exemples d'annotation de Jackson

1. Vue d'ensemble

Dans cet article, nous allons vous plonger dans lesJackson Annotations.

Nous verrons comment utiliser les annotations existantes, comment en créer des personnalisées et enfin - comment les désactiver.

Lectures complémentaires:

Plus d'annotations de Jackson

Cet article couvre certaines annotations de traitement JSON moins connues fournies par Jackson.

Read more

Jackson - Relations bidirectionnelles

Comment utiliser Jackson pour résoudre le problème de la récursion infinie sur les relations bidirectionnelles.

Read more

Débuter avec la désérialisation personnalisée à Jackson

Utilisez Jackson pour mapper le JSON personnalisé à n’importe quel graphe d’entités java avec un contrôle total sur le processus de désérialisation.

Read more

2. Annotations de sérialisation Jackson

Tout d'abord, nous allons examiner les annotations de sérialisation.

2.1. @JsonAnyGetter

L'annotation@JsonAnyGetter permet la flexibilité d'utiliser un champMap comme propriétés standard.

Voici un exemple rapide - l'entitéExtendableBean a la propriéténame et un ensemble d'attributs extensibles sous la forme de paires clé / valeur:

public class ExtendableBean {
    public String name;
    private Map properties;

    @JsonAnyGetter
    public Map getProperties() {
        return properties;
    }
}

Lorsque nous sérialisons une instance de cette entité, nous obtenons toutes les valeurs-clés desMap en tant que propriétés standard et simples:

{
    "name":"My bean",
    "attr2":"val2",
    "attr1":"val1"
}

Et voici à quoi ressemble en pratique la sérialisation de cette entité:

@Test
public void whenSerializingUsingJsonAnyGetter_thenCorrect()
  throws JsonProcessingException {

    ExtendableBean bean = new ExtendableBean("My bean");
    bean.add("attr1", "val1");
    bean.add("attr2", "val2");

    String result = new ObjectMapper().writeValueAsString(bean);

    assertThat(result, containsString("attr1"));
    assertThat(result, containsString("val1"));
}

On peut aussi utiliser l'argument optionnelenabled commefalse pour désactiver@JsonAnyGetter(). Dans ce cas, lesMap seront convertis en JSON et apparaîtront sous la variableproperties après sérialisation.

2.2. @JsonGetter

L'annotation@JsonGetter est une alternative à l'annotation@JsonProperty pour marquer une méthode comme méthode getter.

Dans l'exemple suivant, nous spécifions la méthodegetTheName() comme méthode getter de la propriéténame de l'entitéMyBean:

public class MyBean {
    public int id;
    private String name;

    @JsonGetter("name")
    public String getTheName() {
        return name;
    }
}

Et voici comment cela fonctionne dans la pratique:

@Test
public void whenSerializingUsingJsonGetter_thenCorrect()
  throws JsonProcessingException {

    MyBean bean = new MyBean(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));
}

2.3. @JsonPropertyOrder

Nous pouvons utiliser l'annotation@JsonPropertyOrder pour spécifierthe order of properties on serialization.

Définissons un ordre personnalisé pour les propriétés d'une entitéMyBean:

@JsonPropertyOrder({ "name", "id" })
public class MyBean {
    public int id;
    public String name;
}

Et voici la sortie de la sérialisation:

{
    "name":"My bean",
    "id":1
}

Et un test simple:

@Test
public void whenSerializingUsingJsonPropertyOrder_thenCorrect()
  throws JsonProcessingException {

    MyBean bean = new MyBean(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);
    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));
}

Nous pouvons également utiliser@JsonPropertyOrder(alphabetic=true) pour classer les propriétés par ordre alphabétique. Et dans ce cas, la sortie de la sérialisation sera:

{
    "id":1,
    "name":"My bean"
}

2.4. @JsonRawValue

L'annotation@JsonRawValue peutinstruct Jackson to serialize a property exactly as is.

Dans l'exemple suivant, nous utilisons@JsonRawValue pour incorporer du JSON personnalisé en tant que valeur d'une entité:

public class RawBean {
    public String name;

    @JsonRawValue
    public String json;
}

La sortie de la sérialisation de l'entité est la suivante:

{
    "name":"My bean",
    "json":{
        "attr":false
    }
}

Et un test simple:

@Test
public void whenSerializingUsingJsonRawValue_thenCorrect()
  throws JsonProcessingException {

    RawBean bean = new RawBean("My bean", "{\"attr\":false}");

    String result = new ObjectMapper().writeValueAsString(bean);
    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("{\"attr\":false}"));
}

Nous pouvons également utiliser l'argument booléen optionnelvalue qui définit si cette annotation est active ou non.

2.5. @JsonValue

@JsonValue indique une seule méthode que la bibliothèque utilisera pour sérialiser l'instance entière.

Par exemple, dans une énumération, nous annotons lesgetName avec@JsonValue afin qu'une telle entité soit sérialisée via son nom:

public enum TypeEnumWithValue {
    TYPE1(1, "Type A"), TYPE2(2, "Type 2");

    private Integer id;
    private String name;

    // standard constructors

    @JsonValue
    public String getName() {
        return name;
    }
}

Notre test:

@Test
public void whenSerializingUsingJsonValue_thenCorrect()
  throws JsonParseException, IOException {

    String enumAsString = new ObjectMapper()
      .writeValueAsString(TypeEnumWithValue.TYPE1);

    assertThat(enumAsString, is(""Type A""));
}

2.6. @JsonRootName

L'annotation@JsonRootName est utilisée - si l'encapsulation est activée - pour spécifier le nom du wrapper racine à utiliser.

Le wrapping signifie qu'au lieu de sérialiser unUser en quelque chose comme:

{
    "id": 1,
    "name": "John"
}

Il va être emballé comme ceci:

{
    "User": {
        "id": 1,
        "name": "John"
    }
}

Alors, regardons un exemple -we’re going to use the @JsonRootName annotation to indicate the name of this potential wrapper entity:

@JsonRootName(value = "user")
public class UserWithRoot {
    public int id;
    public String name;
}

Par défaut, le nom du wrapper serait le nom de la classe -UserWithRoot. En utilisant l'annotation, nous obtenons lesuser: plus propres

@Test
public void whenSerializingUsingJsonRootName_thenCorrect()
  throws JsonProcessingException {

    UserWithRoot user = new User(1, "John");

    ObjectMapper mapper = new ObjectMapper();
    mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    String result = mapper.writeValueAsString(user);

    assertThat(result, containsString("John"));
    assertThat(result, containsString("user"));
}

Voici la sortie de la sérialisation:

{
    "user":{
        "id":1,
        "name":"John"
    }
}

Depuis Jackson 2.4, un nouvel argument optionnelnamespace est disponible pour être utilisé avec des formats de données tels que XML. Si nous l’ajoutons, cela fera partie du nom complet:

@JsonRootName(value = "user", namespace="users")
public class UserWithRootNamespace {
    public int id;
    public String name;

    // ...
}

Si nous le sérialisons avecXmlMapper, la sortie sera:


    1
    John
    

2.7. @JsonSerialize

@JsonSerialize indicates a custom serializer to use when rassemblementthe entity.

Prenons un exemple rapide. Nous allons utiliser@JsonSerialize pour sérialiser la propriétéeventDate avec unCustomDateSerializer:

public class EventWithSerializer {
    public String name;

    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
}

Voici le sérialiseur Jackson personnalisé simple:

public class CustomDateSerializer extends StdSerializer {

    private static SimpleDateFormat formatter
      = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateSerializer() {
        this(null);
    }

    public CustomDateSerializer(Class t) {
        super(t);
    }

    @Override
    public void serialize(
      Date value, JsonGenerator gen, SerializerProvider arg2)
      throws IOException, JsonProcessingException {
        gen.writeString(formatter.format(value));
    }
}

Utilisons-les dans un test:

@Test
public void whenSerializingUsingJsonSerialize_thenCorrect()
  throws JsonProcessingException, ParseException {

    SimpleDateFormat df
      = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    EventWithSerializer event = new EventWithSerializer("party", date);

    String result = new ObjectMapper().writeValueAsString(event);
    assertThat(result, containsString(toParse));
}

3. Annotations de désérialisation Jackson

Ensuite, explorons les annotations de désérialisation Jackson.

3.1. @JsonCreator

Nous pouvons utiliser l'annotation@JsonCreator pour régler le constructeur / l'usine utilisé dans la désérialisation.

C'est très utile lorsque nous devons désérialiser du JSON qui ne correspond pas exactement à l'entité cible que nous devons obtenir.

Prenons un exemple; disons que nous devons désérialiser le JSON suivant:

{
    "id":1,
    "theName":"My bean"
}

Cependant, il n'y a pas de champtheName dans notre entité cible - il n'y a qu'un champname. Maintenant, nous ne voulons pas changer l'entité elle-même - nous avons juste besoin d'un peu plus de contrôle sur le processus de démarshalling - en annotant le constructeur avec@JsonCreator et en utilisant également l'annotation@JsonProperty:

public class BeanWithCreator {
    public int id;
    public String name;

    @JsonCreator
    public BeanWithCreator(
      @JsonProperty("id") int id,
      @JsonProperty("theName") String name) {
        this.id = id;
        this.name = name;
    }
}

Voyons cela en action:

@Test
public void whenDeserializingUsingJsonCreator_thenCorrect()
  throws IOException {

    String json = "{\"id\":1,\"theName\":\"My bean\"}";

    BeanWithCreator bean = new ObjectMapper()
      .readerFor(BeanWithCreator.class)
      .readValue(json);
    assertEquals("My bean", bean.name);
}

3.2. @JacksonInject

@JacksonInject indique qu'une propriété obtiendra sa valeur de l'injection et non des données JSON.

Dans l'exemple suivant - nous utilisons@JacksonInject pour injecter la propriétéid:

public class BeanWithInject {
    @JacksonInject
    public int id;

    public String name;
}

Et voici comment cela fonctionne:

@Test
public void whenDeserializingUsingJsonInject_thenCorrect()
  throws IOException {

    String json = "{\"name\":\"My bean\"}";

    InjectableValues inject = new InjectableValues.Std()
      .addValue(int.class, 1);
    BeanWithInject bean = new ObjectMapper().reader(inject)
      .forType(BeanWithInject.class)
      .readValue(json);

    assertEquals("My bean", bean.name);
    assertEquals(1, bean.id);
}

3.3. @JsonAnySetter

@JsonAnySetter nous donne la flexibilité d'utiliser unMap comme propriétés standard. Lors de la désérialisation, les propriétés de JSON seront simplement ajoutées à la carte.

Voyons comment cela fonctionne - nous utiliserons@JsonAnySetter pour désérialiser l'entitéExtendableBean:

public class ExtendableBean {
    public String name;
    private Map properties;

    @JsonAnySetter
    public void add(String key, String value) {
        properties.put(key, value);
    }
}

C'est le JSON que nous devons désérialiser:

{
    "name":"My bean",
    "attr2":"val2",
    "attr1":"val1"
}

Et voici comment tout cela est lié:

@Test
public void whenDeserializingUsingJsonAnySetter_thenCorrect()
  throws IOException {
    String json
      = "{\"name\":\"My bean\",\"attr2\":\"val2\",\"attr1\":\"val1\"}";

    ExtendableBean bean = new ObjectMapper()
      .readerFor(ExtendableBean.class)
      .readValue(json);

    assertEquals("My bean", bean.name);
    assertEquals("val2", bean.getProperties().get("attr2"));
}

3.4. @JsonSetter

@JsonSetter est une alternative à@JsonProperty - qui marque la méthode comme méthode setter.

C'est très utile lorsque nous avons besoin de lire des données JSON maisthe target entity class doesn’t exactly match that data, et que nous devons donc ajuster le processus pour l'adapter.

Dans l'exemple suivant, nous allons spécifier la méthode setTheName() comme setter de la propriéténame dans notre entitéMyBean:

public class MyBean {
    public int id;
    private String name;

    @JsonSetter("name")
    public void setTheName(String name) {
        this.name = name;
    }
}

Maintenant, lorsque nous devons analyser certaines données JSON, cela fonctionne parfaitement:

@Test
public void whenDeserializingUsingJsonSetter_thenCorrect()
  throws IOException {

    String json = "{\"id\":1,\"name\":\"My bean\"}";

    MyBean bean = new ObjectMapper()
      .readerFor(MyBean.class)
      .readValue(json);
    assertEquals("My bean", bean.getTheName());
}

3.5. @JsonDeserialize

@JsonDeserialize indique l'utilisation d'un désérialiseur personnalisé.

Voyons comment cela se passe - nous utiliserons@JsonDeserialize pour désérialiser la propriétéeventDate avec lesCustomDateDeserializer:

public class EventWithSerializer {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}

Voici le désérialiseur personnalisé:

public class CustomDateDeserializer
  extends StdDeserializer {

    private static SimpleDateFormat formatter
      = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateDeserializer() {
        this(null);
    }

    public CustomDateDeserializer(Class vc) {
        super(vc);
    }

    @Override
    public Date deserialize(
      JsonParser jsonparser, DeserializationContext context)
      throws IOException {

        String date = jsonparser.getText();
        try {
            return formatter.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

Et voici le test dos à dos:

@Test
public void whenDeserializingUsingJsonDeserialize_thenCorrect()
  throws IOException {

    String json
      = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";

    SimpleDateFormat df
      = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    EventWithSerializer event = new ObjectMapper()
      .readerFor(EventWithSerializer.class)
      .readValue(json);

    assertEquals(
      "20-12-2014 02:30:00", df.format(event.eventDate));
}

3.6 @JsonAlias

Le@JsonAlias définitone or more alternative names for a property during deserialization.

Voyons comment cette annotation fonctionne avec un exemple rapide:

public class AliasBean {
    @JsonAlias({ "fName", "f_name" })
    private String firstName;
    private String lastName;
}

Ici, nous avons un POJO et nous voulons désérialiser JSON avec des valeurs telles quefName,f_name etfirstName dans la variablefirstName du POJO.

Et voici un test pour vous assurer que cette annotation fonctionne comme prévu:

@Test
public void whenDeserializingUsingJsonAlias_thenCorrect() throws IOException {
    String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
    AliasBean aliasBean = new ObjectMapper().readerFor(AliasBean.class).readValue(json);
    assertEquals("John", aliasBean.getFirstName());
}

4. Annotations d'inclusion de propriété Jackson

4.1. @JsonIgnoreProperties

@JsonIgnoreProperties est une annotation au niveau de la classe qui marque une propriété ou une liste de propriétés que Jackson ignorera.

Prenons un exemple rapide en ignorant la propriétéid de la sérialisation:

@JsonIgnoreProperties({ "id" })
public class BeanWithIgnore {
    public int id;
    public String name;
}

Et voici le test pour s'assurer que l'ignorance se produit:

@Test
public void whenSerializingUsingJsonIgnoreProperties_thenCorrect()
  throws JsonProcessingException {

    BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");

    String result = new ObjectMapper()
      .writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, not(containsString("id")));
}

Pour ignorer toutes les propriétés inconnues dans l'entrée JSON sans exception, nous pouvons définirignoreUnknown=true de l'annotation@JsonIgnoreProperties .

4.2. @JsonIgnore

L'annotation@JsonIgnore est utilisée pour marquer une propriété à ignorer au niveau du champ.

Utilisons@JsonIgnore pour ignorer la propriétéid de la sérialisation:

public class BeanWithIgnore {
    @JsonIgnore
    public int id;

    public String name;
}

Et le test s'assurant queid a été ignoré avec succès:

@Test
public void whenSerializingUsingJsonIgnore_thenCorrect()
  throws JsonProcessingException {

    BeanWithIgnore bean = new BeanWithIgnore(1, "My bean");

    String result = new ObjectMapper()
      .writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, not(containsString("id")));
}

4.3. @JsonIgnoreType

@JsonIgnoreType marque toutes les propriétés d'un type annoté à ignorer.

Utilisons l'annotation pour marquer toutes les propriétés de typeName à ignorer:

public class User {
    public int id;
    public Name name;

    @JsonIgnoreType
    public static class Name {
        public String firstName;
        public String lastName;
    }
}

Voici un test simple pour vous assurer que l'ignorer fonctionne correctement:

@Test
public void whenSerializingUsingJsonIgnoreType_thenCorrect()
  throws JsonProcessingException, ParseException {

    User.Name name = new User.Name("John", "Doe");
    User user = new User(1, name);

    String result = new ObjectMapper()
      .writeValueAsString(user);

    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("name")));
    assertThat(result, not(containsString("John")));
}

4.4. @JsonInclude

Nous pouvons utiliser@JsonInclude pour exclure les propriétés avec des valeurs vides / nulles / par défaut.

Prenons un exemple - excluant les valeurs nulles de la sérialisation:

@JsonInclude(Include.NON_NULL)
public class MyBean {
    public int id;
    public String name;
}

Voici le test complet:

public void whenSerializingUsingJsonInclude_thenCorrect()
  throws JsonProcessingException {

    MyBean bean = new MyBean(1, null);

    String result = new ObjectMapper()
      .writeValueAsString(bean);

    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("name")));
}

4.5. @JsonAutoDetect

@JsonAutoDetect peut remplacer la sémantique par défaut dewhich properties are visible and which are not.

Voyons comment l'annotation peut être très utile avec un exemple simple - activons la sérialisation des propriétés privées:

@JsonAutoDetect(fieldVisibility = Visibility.ANY)
public class PrivateBean {
    private int id;
    private String name;
}

Et le test:

@Test
public void whenSerializingUsingJsonAutoDetect_thenCorrect()
  throws JsonProcessingException {

    PrivateBean bean = new PrivateBean(1, "My bean");

    String result = new ObjectMapper()
      .writeValueAsString(bean);

    assertThat(result, containsString("1"));
    assertThat(result, containsString("My bean"));
}

5. Annotations de gestion de type polymorphe Jackson

Ensuite, examinons les annotations de gestion des types polymorphes Jackson:

  • @JsonTypeInfo - indique les détails des informations de type à inclure dans la sérialisation

  • @JsonSubTypes - indique les sous-types du type annoté

  • @JsonTypeName - définit un nom de type logique à utiliser pour la classe annotée

Examinons un exemple plus complexe et utilisons les trois -@JsonTypeInfo,@JsonSubTypes, et@JsonTypeName – pour sérialiser / désérialiser l'entitéZoo:

public class Zoo {
    public Animal animal;

    @JsonTypeInfo(
      use = JsonTypeInfo.Id.NAME,
      include = As.PROPERTY,
      property = "type")
    @JsonSubTypes({
        @JsonSubTypes.Type(value = Dog.class, name = "dog"),
        @JsonSubTypes.Type(value = Cat.class, name = "cat")
    })
    public static class Animal {
        public String name;
    }

    @JsonTypeName("dog")
    public static class Dog extends Animal {
        public double barkVolume;
    }

    @JsonTypeName("cat")
    public static class Cat extends Animal {
        boolean likesCream;
        public int lives;
    }
}

Quand on fait la sérialisation:

@Test
public void whenSerializingPolymorphic_thenCorrect()
  throws JsonProcessingException {
    Zoo.Dog dog = new Zoo.Dog("lacy");
    Zoo zoo = new Zoo(dog);

    String result = new ObjectMapper()
      .writeValueAsString(zoo);

    assertThat(result, containsString("type"));
    assertThat(result, containsString("dog"));
}

Voici ce que la sérialisation de l'instanceZoo avec lesDog entraînera:

{
    "animal": {
        "type": "dog",
        "name": "lacy",
        "barkVolume": 0
    }
}

Passons maintenant à la désérialisation - commençons par l'entrée JSON suivante:

{
    "animal":{
        "name":"lacy",
        "type":"cat"
    }
}

Et voyons comment cela est annulé dans une instanceZoo:

@Test
public void whenDeserializingPolymorphic_thenCorrect()
throws IOException {
    String json = "{\"animal\":{\"name\":\"lacy\",\"type\":\"cat\"}}";

    Zoo zoo = new ObjectMapper()
      .readerFor(Zoo.class)
      .readValue(json);

    assertEquals("lacy", zoo.animal.name);
    assertEquals(Zoo.Cat.class, zoo.animal.getClass());
}

6. Annotations générales de Jackson

Ensuite, discutons de certaines des annotations plus générales de Jackson.

6.1. @JsonProperty

Nous pouvons ajouterthe @JsonProperty annotation to indicate the property name in JSON.

Utilisons@JsonProperty pour sérialiser / désérialiser la propriéténame lorsque nous avons affaire à des getters et des setters non standard:

public class MyBean {
    public int id;
    private String name;

    @JsonProperty("name")
    public void setTheName(String name) {
        this.name = name;
    }

    @JsonProperty("name")
    public String getTheName() {
        return name;
    }
}

Notre test:

@Test
public void whenUsingJsonProperty_thenCorrect()
  throws IOException {
    MyBean bean = new MyBean(1, "My bean");

    String result = new ObjectMapper().writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));

    MyBean resultBean = new ObjectMapper()
      .readerFor(MyBean.class)
      .readValue(result);
    assertEquals("My bean", resultBean.getTheName());
}

6.2. @JsonFormat

L'annotation@JsonFormat spécifie un format lors de la sérialisation des valeurs de date / heure.

Dans l'exemple suivant, nous utilisons@JsonFormat pour contrôler le format de la propriétéeventDate:

public class EventWithFormat {
    public String name;

    @JsonFormat(
      shape = JsonFormat.Shape.STRING,
      pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
}

Et voici le test:

@Test
public void whenSerializingUsingJsonFormat_thenCorrect()
  throws JsonProcessingException, ParseException {
    SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    df.setTimeZone(TimeZone.getTimeZone("UTC"));

    String toParse = "20-12-2014 02:30:00";
    Date date = df.parse(toParse);
    EventWithFormat event = new EventWithFormat("party", date);

    String result = new ObjectMapper().writeValueAsString(event);

    assertThat(result, containsString(toParse));
}

6.3. @JsonUnwrapped

@JsonUnwrapped définit les valeurs qui doivent être déballées / aplaties lorsqu'elles sont sérialisées / désérialisées.

Voyons exactement comment cela fonctionne; nous utiliserons l'annotation pour dérouler la propriéténame:

public class UnwrappedUser {
    public int id;

    @JsonUnwrapped
    public Name name;

    public static class Name {
        public String firstName;
        public String lastName;
    }
}

Sérialisons maintenant une instance de cette classe:

@Test
public void whenSerializingUsingJsonUnwrapped_thenCorrect()
  throws JsonProcessingException, ParseException {
    UnwrappedUser.Name name = new UnwrappedUser.Name("John", "Doe");
    UnwrappedUser user = new UnwrappedUser(1, name);

    String result = new ObjectMapper().writeValueAsString(user);

    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("name")));
}

Voici à quoi ressemble la sortie - les champs de la classe imbriquée statique sont déroulés avec l'autre champ:

{
    "id":1,
    "firstName":"John",
    "lastName":"Doe"
}

6.4. @JsonView

@JsonView indique la vue dans laquelle la propriété sera incluse pour la sérialisation / désérialisation.

Un exemple montrera exactement comment cela fonctionne - nous utiliserons@JsonView pour sérialiser une instance de l'entitéItem.

Commençons par les vues:

public class Views {
    public static class Public {}
    public static class Internal extends Public {}
}

Et maintenant, voici l'entitéItem, en utilisant les vues:

public class Item {
    @JsonView(Views.Public.class)
    public int id;

    @JsonView(Views.Public.class)
    public String itemName;

    @JsonView(Views.Internal.class)
    public String ownerName;
}

Enfin - le test complet:

@Test
public void whenSerializingUsingJsonView_thenCorrect()
  throws JsonProcessingException {
    Item item = new Item(2, "book", "John");

    String result = new ObjectMapper()
      .writerWithView(Views.Public.class)
      .writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("2"));
    assertThat(result, not(containsString("John")));
}

6.5. @JsonManagedReference, @JsonBackReference

The @JsonManagedReference and @JsonBackReference annotations can handle parent/child relationships et contourner les boucles.

Dans l'exemple suivant, nous utilisons@JsonManagedReference et@JsonBackReference pour sérialiser notre entitéItemWithRef:

public class ItemWithRef {
    public int id;
    public String itemName;

    @JsonManagedReference
    public UserWithRef owner;
}

Notre entitéUserWithRef:

public class UserWithRef {
    public int id;
    public String name;

    @JsonBackReference
    public List userItems;
}

Et le test:

@Test
public void whenSerializingUsingJacksonReferenceAnnotation_thenCorrect()
  throws JsonProcessingException {
    UserWithRef user = new UserWithRef(1, "John");
    ItemWithRef item = new ItemWithRef(2, "book", user);
    user.addItem(item);

    String result = new ObjectMapper().writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, not(containsString("userItems")));
}

6.6. @JsonIdentityInfo

@JsonIdentityInfo indique que l'identité d'objet doit être utilisée lors de la sérialisation / désérialisation des valeurs - par exemple, pour traiter des problèmes de type récursivité infinie.

Dans l'exemple suivant - nous avons une entitéItemWithIdentity avec une relation bidirectionnellea avec l'entitéUserWithIdentity:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class ItemWithIdentity {
    public int id;
    public String itemName;
    public UserWithIdentity owner;
}

Et l'entitéUserWithIdentity:

@JsonIdentityInfo(
  generator = ObjectIdGenerators.PropertyGenerator.class,
  property = "id")
public class UserWithIdentity {
    public int id;
    public String name;
    public List userItems;
}

Maintenant,let’s see how the infinite recursion problem is handled:

@Test
public void whenSerializingUsingJsonIdentityInfo_thenCorrect()
  throws JsonProcessingException {
    UserWithIdentity user = new UserWithIdentity(1, "John");
    ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
    user.addItem(item);

    String result = new ObjectMapper().writeValueAsString(item);

    assertThat(result, containsString("book"));
    assertThat(result, containsString("John"));
    assertThat(result, containsString("userItems"));
}

Voici la sortie complète de l'élément sérialisé et de l'utilisateur:

{
    "id": 2,
    "itemName": "book",
    "owner": {
        "id": 1,
        "name": "John",
        "userItems": [
            2
        ]
    }
}

6.7. @JsonFilter

L'annotation@JsonFilter spécifie un filtre à utiliser lors de la sérialisation.

Prenons un exemple; tout d'abord, nous définissons l'entité, et nous pointons vers le filtre:

@JsonFilter("myFilter")
public class BeanWithFilter {
    public int id;
    public String name;
}

Maintenant, dans le test complet, nous définissons le filtre - qui exclut toutes les autres propriétés saufname de la sérialisation:

@Test
public void whenSerializingUsingJsonFilter_thenCorrect()
  throws JsonProcessingException {
    BeanWithFilter bean = new BeanWithFilter(1, "My bean");

    FilterProvider filters
      = new SimpleFilterProvider().addFilter(
        "myFilter",
        SimpleBeanPropertyFilter.filterOutAllExcept("name"));

    String result = new ObjectMapper()
      .writer(filters)
      .writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, not(containsString("id")));
}

7. Annotation Jackson personnalisée

Voyons ensuite comment créer une annotation Jackson personnalisée. We can make use of the @JacksonAnnotationsInside annotation:

@Retention(RetentionPolicy.RUNTIME)
    @JacksonAnnotationsInside
    @JsonInclude(Include.NON_NULL)
    @JsonPropertyOrder({ "name", "id", "dateCreated" })
    public @interface CustomAnnotation {}

Maintenant, si nous utilisons la nouvelle annotation sur une entité:

@CustomAnnotation
public class BeanWithCustomAnnotation {
    public int id;
    public String name;
    public Date dateCreated;
}

Nous pouvons voir comment il combine les annotations existantes dans un annuaire personnalisé plus simple que nous pouvons utiliser comme raccourci:

@Test
public void whenSerializingUsingCustomAnnotation_thenCorrect()
  throws JsonProcessingException {
    BeanWithCustomAnnotation bean
      = new BeanWithCustomAnnotation(1, "My bean", null);

    String result = new ObjectMapper().writeValueAsString(bean);

    assertThat(result, containsString("My bean"));
    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("dateCreated")));
}

La sortie du processus de sérialisation:

{
    "name":"My bean",
    "id":1
}

8. Annotations Jackson MixIn

Ensuite, voyons comment utiliser les annotations Jackson MixIn.

Utilisons les annotations MixIn pour - par exemple - ignorer les propriétés de typeUser:

public class Item {
    public int id;
    public String itemName;
    public User owner;
}
@JsonIgnoreType
public class MyMixInForIgnoreType {}

Voyons cela en action:

@Test
public void whenSerializingUsingMixInAnnotation_thenCorrect()
  throws JsonProcessingException {
    Item item = new Item(1, "book", null);

    String result = new ObjectMapper().writeValueAsString(item);
    assertThat(result, containsString("owner"));

    ObjectMapper mapper = new ObjectMapper();
    mapper.addMixIn(User.class, MyMixInForIgnoreType.class);

    result = mapper.writeValueAsString(item);
    assertThat(result, not(containsString("owner")));
}

9. Désactiver l'annotation Jackson

Enfin, voyons comment nous pouvonsdisable all Jackson annotations. Nous pouvons le faire en désactivant lesMapperFeature.USE_ANNOTATIONS comme dans l'exemple suivant:

@JsonInclude(Include.NON_NULL)
@JsonPropertyOrder({ "name", "id" })
public class MyBean {
    public int id;
    public String name;
}

Maintenant, après la désactivation des annotations, celles-ci ne devraient avoir aucun effet et les valeurs par défaut de la bibliothèque devraient s'appliquer:

@Test
public void whenDisablingAllAnnotations_thenAllDisabled()
  throws IOException {
    MyBean bean = new MyBean(1, null);

    ObjectMapper mapper = new ObjectMapper();
    mapper.disable(MapperFeature.USE_ANNOTATIONS);
    String result = mapper.writeValueAsString(bean);

    assertThat(result, containsString("1"));
    assertThat(result, containsString("name"));
}

Résultat de la sérialisation avant la désactivation des annotations:

{"id":1}

Résultat de la sérialisation après la désactivation des annotations:

{
    "id":1,
    "name":null
}

10. Conclusion

Ce tutoriel a été une plongée dans les annotations de Jackson, il n’a fait qu’effleurer le genre de flexibilité que vous pouvez obtenir en les utilisant correctement.

L'implémentation de tous ces exemples et extraits de code se trouve dans leGitHub project - il s'agit d'un projet basé sur Maven, il devrait donc être facile à importer et à exécuter tel quel.