Jackson - Décidez quels champs sont sérialisés/désérialisés

Jackson - Décidez quels champs doivent être sérialisés / désérialisés

1. Vue d'ensemble

Dans cet article, nous allons explorer les différentes façons dont nous pouvons contrôler ou nonif a field is serialized / deserialized by Jackson.

2. Un champ public

Le moyen le plus simple de s'assurer qu'un champ est à la fois sérialisable et désérialisable est de le rendre public.

Déclarons une classe simple avec un public, un package-private et un private

public class MyDtoAccessLevel {
    private String stringValue;
    int intValue;
    protected float floatValue;
    public boolean booleanValue;
    // NO setters or getters
}

Sur les quatre champs de la classe, seuls lesbooleanValuepublics seront sérialisés en JSON par défaut:

@Test
public void givenDifferentAccessLevels_whenPublic_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("stringValue")));
    assertThat(dtoAsString, not(containsString("intValue")));
    assertThat(dtoAsString, not(containsString("floatValue")));

    assertThat(dtoAsString, containsString("booleanValue"));
}

3. Un Getter rend un champ non public sérialisable et désérialisable

Maintenant, un autre moyen simple de rendre un champ - en particulier un champ non public - sérialisable, consiste à lui ajouter un getter:

public class MyDtoWithGetter {
    private String stringValue;
    private int intValue;

    public String getStringValue() {
        return stringValue;
    }
}

Nous nous attendons maintenant à ce que le champstringValue soit sérialisable, tandis que l'autre champ privé ne le sera pas, car il n'a pas de getter:

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoGetter dtoObject = new MyDtoGetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, not(containsString("intValue")));
}

De manière non intuitive, le getter rend également le champ privé désérialisable - car une fois qu’il a un getter, le champ est considéré comme une propriété.

Voyons comment cela fonctionne:

@Test
public void givenDifferentAccessLevels_whenGetterAdded_thenDeserializable()
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"stringValue\":\"dtoString\"}";
    ObjectMapper mapper = new ObjectMapper();
    MyDtoWithGetter dtoObject = mapper.readValue(jsonAsString, MyDtoWithGetter.class);

    assertThat(dtoObject.getStringValue(), equalTo("dtoString"));
}

4. Un Setter rend un champ non public désérialisable uniquement

Nous avons vu comment le getter rendait le champ privé sérialisable et désérialisable. D'autre part, un séparateur marquera uniquement le champ non public comme étant désérialisable:

public class MyDtoWithSetter {
    private int intValue;

    public void setIntValue(int intValue) {
        this.intValue = intValue;
    }

    public int accessIntValue() {
        return intValue;
    }
}

Comme vous pouvez le voir, le champ privéintValue n'a cette fois qu'un setter. Nous avons un moyen d'accéder à la valeur, mais ce n'est pas un getter standard.

Le processus de démarshalling pourintValue devrait fonctionner correctement:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenDeserializable()
  throws JsonProcessingException, JsonMappingException, IOException {
    String jsonAsString = "{\"intValue\":1}";
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = mapper.readValue(jsonAsString, MyDtoSetter.class);

    assertThat(dtoObject.anotherGetIntValue(), equalTo(1));
}

Et, comme nous l’avons mentionné, le séparateur doit uniquement rendre le champ désérialisable, mais pas sérialisable:

@Test
public void givenDifferentAccessLevels_whenSetterAdded_thenStillNotSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    MyDtoSetter dtoObject = new MyDtoSetter();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, not(containsString("intValue")));
}

5. Rendre tous les champs globalement sérialisables

Dans certains cas, par exemple, vous ne pourrez peut-être pas modifier directement le code source - nous devons configurer la manière dont Jackson traite les champs non publics de l'extérieur.

Ce type de configuration globale peut être effectué au niveau d'ObjectMapper, en activant la fonctionAutoDetect pour utiliser les champspublic ou des méthodes getter / setter pour la sérialisation, ou peut-être activer la sérialisation pour tous les champs:

ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

Le cas de test suivant vérifie que tous les champs membres (y compris non publics) deMyDtoAccessLevel sont sérialisables:

@Test
public void givenDifferentAccessLevels_whenSetVisibility_thenSerializable()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);

    MyDtoAccessLevel dtoObject = new MyDtoAccessLevel();

    String dtoAsString = mapper.writeValueAsString(dtoObject);
    assertThat(dtoAsString, containsString("stringValue"));
    assertThat(dtoAsString, containsString("intValue"));
    assertThat(dtoAsString, containsString("booleanValue"));
}

6. Modifier le nom d'une propriété lors de la sérialisation / désérialisation

Au-delà du contrôle du champ qui est sérialisé ou désérialisé, vous pouvez égalementhave control over the way a fields maps to JSON and back. J'ai couvertthis configuration here.

7. Ignorer un champ lors de la sérialisation ou de la désérialisation

Aprèsthis tutorial, nous avons un guide pour ignorer complètement un champ lors de la sérialisation et de la désérialisation.

Cependant, il est parfois nécessaire d'ignorer le champ sur l'un ou l'autre, mais pas sur les deux. Jackson est suffisamment flexible pour accueillir cet intéressant cas d'utilisation.

L'exemple suivant montre un objetUser qui contient des informations de mot de passe sensibles qui ne doivent pas être sérialisées en JSON.

Pour y arriver, nous ajoutons simplement l'annotation@JsonIgnore sur le getter dupassword, et activons la désérialisation du champ en appliquant l'annotation@JsonProperty sur le setter:

@JsonIgnore
public String getPassword() {
    return password;
}
@JsonProperty
public void setPassword(String password) {
    this.password = password;
}

Les informations de mot de passe ne seront pas sérialisées en JSON:

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsSerialized_thenIgnored()
  throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();

    User userObject = new User();
    userObject.setPassword("thePassword");

    String userAsString = mapper.writeValueAsString(userObject);
    assertThat(userAsString, not(containsString("password")));
    assertThat(userAsString, not(containsString("thePassword")));
}

Cependant, le JSON contenant le mot de passe sera désérialisé avec succès vers l'objetUser:

@Test
public void givenFieldTypeIsIgnoredOnlyAtSerialization_whenUserIsDeserialized_thenCorrect()
  throws JsonParseException, JsonMappingException, IOException {
    String jsonAsString = "{\"password\":\"thePassword\"}";
    ObjectMapper mapper = new ObjectMapper();

    User userObject = mapper.readValue(jsonAsString, User.class);

    assertThat(userObject.getPassword(), equalTo("thePassword"));
}

8. Conclusion

Ce didacticiel explique comment Jackson choisitwhich field is serialized/deserialized and which gets ignored dans le processus et, bien sûr, comment en obtenir le contrôle total.

Vous pouvez également passer à l'étape suivante dans la compréhension de Jackson 2 en plongeant plus profondément avec des articles tels queignoring a field,deserializing a JSON Array to a Java Array or Collection.

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