web-dev-qa-db-fra.com

Bibliothèque Jackson JSON: comment instancier une classe contenant des champs abstraits

Je souhaite convertir une chaîne JSON en objet Java, mais la classe de cet objet contient des champs abstraits que Jackson ne peut pas instancier et ne produit pas l'objet. Quel est le moyen le plus simple de le dire sur une implémentation par défaut d’une classe abstraite, telle que

setDefault(AbstractAnimal.class, Cat.class);

ou pour décider de la classe d'implémentation en fonction du nom de l'attribut JSON, par exemple. pour objet JSON: 

{
    ...
    cat: {...}
    ...
}

je voudrais juste dire: 

setImpl("cat", Cat.class);


Je sais qu'il est possible à Jackson d'intégrer des informations de classe dans JSON, mais je ne veux pas compliquer le format JSON que j'utilise. Je veux décider de la classe à utiliser simplement en définissant la classe d'implémentation par défaut ou par le nom d'attribut ('cat') - comme dans la bibliothèque XStream, où vous écrivez:

xStream.alias("cat", Cat.class);

Existe-t-il un moyen de le faire, en particulier sur une ligne, ou nécessite-t-il un peu plus de code?

27
Marcin

Il y a plusieurs façons. avant la version 1.8, le moyen le plus simple est probablement de faire:

@JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }

quant à la décision basée sur l'attribut, il est préférable d'utiliser @JsonTypeInfo, qui effectue l'intégration automatique (lors de l'écriture) et l'utilisation des informations de type.

Il existe plusieurs types d'informations de type (nom de classe, nom de type logique), ainsi que des mécanismes d'inclusion (propriété-incluse, propriété-wrapper-array, objet-wrapper-object). Cette page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explique certains des concepts.

38
StaxMan

Vous trouverez une réponse complète avec un exemple très clair ici: https://stackoverflow.com/a/30386694/584947

Jackson appelle cela la désérialisation polymorphe.

Cela m'a définitivement aidé avec mon problème. J'avais une classe abstraite que je sauvegardais dans une base de données et que je devais décompresser en une instance concrète d'une classe (ce qui est compréhensible).

Il vous montrera comment annoter correctement la classe abstraite parent et comment enseigner à Jackson comment choisir parmi les candidats de sous-classes disponibles au moment de l'exécution, lorsque le brassage est dissipé.

2
anon58192932

Si vous ne souhaitez polluer ni votre JSON avec des champs supplémentaires ni vos classes avec des annotations, vous pouvez écrire un module très simple et un désérialiseur utilisant la sous-classe par défaut de votre choix. Il y a plus d'une ligne à cause d'un code standard, mais cela reste relativement simple.

class AnimalDeserializer extends StdDeserializer<Animal> {
    public AnimalDeserializer() {
        super(Animal.class);
    }

    public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
        return jsonParser.readValueAs(Cat.class);
    }
}

class AnimalModule extends SimpleModule {
    {
        addDeserializer(Animal.class, new AnimalDeserializer());
    }
}

Enregistrez ensuite ce module pour ObjectMapper et c'est tout (Zoo est la classe de conteneur qui a un champ Animal).

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
0
István

le problème peut être résolu avec l'annotation @JsonDeserialize sur la classe abstraite fait référence à Jackson Exceptions Problems and Solutions https://www.baeldung.com/jackson-exception

0
zedtimi