web-dev-qa-db-fra.com

Désérialiseur personnalisé Jackson pour un champ avec des types polymorphes

Mise à jour:

J'ai essayé de déboguer dans le code source jackson et de découvrir que dans la méthode

deserialize(JsonParser jp, DeserializationContext ctxt) de

SettableBeanProperty.Java

quand le _valueTypeDeserializer n'est pas nul, il n'utilisera jamais _valueDeserializer.

Est-ce une supposition correcte que TypeDeserializer devrait être plus approprié que ValueDeserializer?


J'essaie d'utiliser @JsonDeserialize pour définir un désérialiseur personnalisé pour un champ avec des types polymorphes. Je peux réussir à utiliser @JsonDeserialize et @JsonTypeInfo séparément. Mais quand je les utilise ensemble, il semble que le @JsonDeserialize l'annotation est ignorée.

Voici ma hiérarchie de classes:

1. Définition.Java

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type", defaultImpl = Definition.class)
@JsonSubTypes({@Type(value = Definition.class, name = "normal"),
    @Type(value = FormDefinition.class, name = "form")})
public class Definition {

    private String name;
    private String description;

    // getter&setter for name&description
}

2. FormDefinition.Java

public class FormDefinition extends Definition {

    private String formName;
    @JsonDeserialize(using = DefinitionDeserializer.class)
    private Definition definition;

    public String getFormName() {
        return formName;
    }

    public void setFormName(String formName) {
        this.formName = formName;
    }

    public void setDefinition(Definition definition) {
        this.definition = definition;
    }
}

3. DefinitionDeserializer.Java

public class DefinitionDeserializer extends JsonDeserializer<Definition> {

    @Override 
    public Definition deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
            throws IOException, JsonProcessingException {
        Definition definition = new Definition();
        String name = jsonParser.readValueAs(String.class);
        definition.setName(name);
        return definition;
    }
}

Exemple principal

public class App 
{
    public static void main( String[] args ) throws IOException {
        String sampleData = "{\"type\":\"form\",\"definition\":\"super\",\"formName\":\"FormName\"}";
        ObjectMapper mapper = new ObjectMapper();
        Definition definition = mapper.readValue(sampleData, Definition.class);
        System.out.println(definition);
    }
}

L'exécution de l'exemple d'application principale génère une exception:

Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class jp.co.worksap.model.Definition] from String value ('super'); no single-String constructor/factory method (through reference chain: jp.co.worksap.model.FormDefinition["definition"])

qui semble utiliser AsPropertyTypeDeserializer pour désérialiser le champ definition de la classe FormDefinition au lieu du désérialiseur annoté DefinitionDeserializer.

Je pense que la partie délicate ici est que le champ que je veux utiliser le désérialiseur personnalisé est également de type Definition qui utilisant @JsonTypeInfo pour résoudre les problèmes polymorphes.

Existe-t-il un moyen de les utiliser ensemble? Merci.

21
xi.lin

Jackson traite @JsonTypeInfo avant de choisir le désérialiseur à utiliser, probablement parce que le choix du désérialiseur peut dépendre généralement du type. Cependant, vous pouvez facilement désactiver cela sur une base par champ de la même manière que vous spécifiez le désérialiseur personnalisé - en annotant directement le champ:

@JsonDeserialize(using = DefinitionDeserializer.class)
@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
private Definition definition;
8
Anton Koscejev