Utilisation de Jackson ObjectMapper avec Java 8 valeurs optionnelles

J'essayais d'utiliser Jackson pour écrire une valeur de classe dans JSON qui a des champs optionnels:

public class Test {
    Optional<String> field = Optional.of("hello, world!");

    public Optional<String> getField() {
        return field;
    }

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(new Test()));
    }
}

Lorsqu'elle est exécutée, cette classe génère la sortie suivante:

{"field":{"present":true}}

Je comprends que le champ présent/non présent soit inclus et pourrait le contourner lors de la lecture des données JSON, mais je ne peux pas contourner le fait que le contenu réel de l'option n'est jamais écrit dans la sortie. :(

Des solutions de contournement ici sauf ne pas utiliser ObjectMapper du tout?

42
demandé sur Sotirios Delimanolis 2014-09-06 00:12:06

7 réponses

Vous pouvez utiliser jackson-type de données-jdk8, qui est décrit comme:

Prise en charge des nouveaux types spécifiques à JDK8, tels que optionnel

47
répondu Jonas 2014-12-03 12:32:13

La classe Optional a un champ value, mais pas de getter/setter standard pour cela. Par défaut, Jackson recherche les getters / setters pour trouver les propriétés de classe.

Vous pouvez ajouter un Mixin personnalisé pour identifier le champ en tant que propriété

final class OptionalMixin {
    private Mixin(){}
    @JsonProperty
    private Object value;
}

Et enregistrez-le avec votre ObjectMapper.

ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(Optional.class, OptionalMixin.class);

Vous pouvez maintenant sérialiser votre objet.

System.out.println(mapper.writeValueAsString(new Test()));

Imprimera

{"field":{"value":"hello, world!","present":true}}

Envisager également de regarder jackson-datatype-guava. Il y a une implémentation Jackson Module pour les types de goyave y compris leur Optional. C'est peut-être plus complet que ce que j'ai montré ci-dessus.

12
répondu Sotirios Delimanolis 2014-09-06 17:42:16

Similaire à la réponse de @Manikandan mais ajoutez @JsonProperty au champ privé au lieu d'un getter afin de ne pas exposer votre travail sur l'api publique.

public class Test {

    @JsonProperty("field")
    private String field;

    @JsonIgnore
    public Optional<String> getField() {
        return Optional.of(field); // or Optional.ofNullable(field);
    }
}
8
répondu mjj1409 2016-07-31 00:41:57

Définissez new getter qui retournera String au lieu D'Optional.

public class Test {
    Optional<String> field = Optional.of("hello, world!");

    @JsonIgnore
    public Optional<String> getField() {
        return field;
    }

    @JsonProperty("field")
    public String getFieldName() {
        return field.orElse(null);
    }

}
3
répondu Manikandan 2014-09-06 17:34:47

Essayez de passer des options dans @JsonInclude annotation.
Par exemple, si vous ne voulez pas afficher field lorsque la valeur est null. vous devrez peut-être utiliser Jackson-Modules > 2.8.5 .

import com.fasterxml.jackson.annotation.JsonInclude;

public class Test {
    @JsonInclude(JsonInclude.Include.NON_NULL)
    Optional<String> field;
}
3
répondu jiantongc 2017-02-16 07:08:05

Si vous utilisez la dernière version de Spring-boot, vous pouvez y parvenir en ajoutant la dépendance suivante dans le fichier pom

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jdk8</artifactId>
</dependency>

Et auto fil le JacksonObjectMapper.

@Autowired
private ObjectMapper jacksonObjectMapper;

Ensuite, utilisez l'instance de mapper du conteneur Spring ci-dessus pour convertir L'objet en chaîne

jacksonObjectMapper.writeValueAsString(user);

Le blog Du printemps dit:

Certains modules Jackson bien connus sont automatiquement enregistrés s'ils sont détectés sur le classpath:

  • jackson-type de données-jdk7: Java 7 types comme java.nio.fichier.Chemin (à partir de la version 4.2.1)
  • jackson-datatype-joda: Joda-types de temps
  • jackson-datatype-Jsr310: Java 8 types de données API Date et heure
  • jackson-datatype-jdk8: D'autres types Java 8 comme optionnels (à partir de la version 4.2.0)
0
répondu seenimurugan 2017-01-18 17:44:10

Il vous suffit d'enregistrer le module Jdk8Module. Ensuite, il sérialisera tout Optional<T> comme {[5] } s'il est présent ou null sinon.

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new Jdk8Module());

Modifions un peu la classe Test pour pouvoir tester une valeur Optional.empty(). Ceci est similaire à la classe Test d'origine lorsqu'elle est sérialisée car le mappeur d'objet recherche le getter (puisque le champ est private). L'utilisation de la classe Test d'origine fonctionnera aussi.

class Test {
    private final String field;

    public Test(String field) {
        this.field = field;
    }

    public Optional<String> getField() {
        return Optional.ofNullable(field);
    }
}

, Puis dans notre classe principale:

Test testFieldNull = new Test(null);
Test testFieldNotNull = new Test("foo");

// output: {"field":null}
System.out.println(objectMapper.writeValueAsString(testFieldNull)); 

// output: {"field":"foo"}
System.out.println(objectMapper.writeValueAsString(testFieldNotNull)); 
0
répondu cakraww 2018-03-11 02:00:09