Написание нестандартного десериализатора для полиморфной c иерархии типов Джексона - PullRequest
3 голосов
/ 01 мая 2020

Я экспериментирую с десериализацией Джексона для наследования в Java. У меня есть базовый класс:

@Getter //Lombok @Getter
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes(value = {
        @JsonSubTypes.Type(value=ClassA.class, name = "classA")
})
public abstract class BaseClass {
   private List<String> fields;

   @JsonCreator
   public BaseClass(@JsonProperty("fields") final List<String> fields) {
     this.fields = fields;
   }
}

ClassA также является абстрактным

@Getter
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "typeA", include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes(value = {
        @JsonSubTypes.Type(value=SubClassA.class, name = "subclassA")
})
public abstract class ClassA extends BaseClass{
   private String mode;

   @JsonCreator
   public ClassA(@JsonProperty("fields") final List<String> fields, @JsonProperty("mode") String mode) {
       super(fields);
       this.mode = mode;
   }
}

Мой подкласс:

public class SubClassA extends ClassA {
   private String dummyField;

   public SubClassA(@JsonProperty("fields") final List<String> fields, @JsonProperty("mode") String mode,
     @JsonProperty("dummyField") String dummyField) {
       super(fields, mode);
       this.dummyField = dummyField;
   }

}

Если я передам JSON из в следующей форме:

{
  "type": "classA",
  "typeA": "subclassA",
  "mode": "testingMode",
  "fields": ["1", "2"],
  "dummyField": "dummy"
}

Я получаю сообщение об ошибке Невозможно создать экземпляр ClassA (не существует создателей, таких как конструкция по умолчанию): абстрактные типы либо должны быть сопоставлены с конкретными типами, либо иметь собственный десериализатор, либо содержат дополнительную информацию о типах, с которой я столкнулся https://github.com/FasterXML/jackson-databind/issues/374, которая говорит, что это известная проблема с Джексоном. Как мне go написать о CustomDeserializer для этого.

В классе А я попытался сделать это:

@JsonDeserialize(using = ClassADeserializer.class)

и ClassADeserializer:

public class ClassADeserializer extends StdDeserializer<ClassA> {
    private final JsonDeserializer<?> defaultDeserializer;
    public ClassADeserializer(JsonDeserializer<?> defaultDeserializer) {
        super(ClassA.class);
        this.defaultDeserializer = defaultDeserializer;
    }

    @Override public ClassA deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
       return (ClassA) defaultDeserializer.deserialize(jsonParser, deserializationContext);
    }

, что, очевидно, не работает. Как мне go написать собственный десериализатор для этого?

1 Ответ

1 голос
/ 01 мая 2020

Проблема:

Вы передаете json "type": "classA", ... Это означает, что Джексон сначала попытается создать экземпляр ClassA .. Во время десериализации поиск Джексона @JsonCreator сначала конструктор .. Если @JsonCreator отсутствует или не может вызвать конструктор @JsonCreator, затем Джексон создает объект с помощью конструктора по умолчанию и метода установки вызова ... В вашем конструкторе ClassA @JsonCreator с 2 аргументами, но вызов Джексона с 3 аргументами. затем Джексон вызывает конструктор по умолчанию для создания экземпляра. но конструктор по умолчанию также отсутствует .. вот почему вы получаете эту ошибку: Cannot construct instance of ClassA (no Creators, like default construct, exist) ..

Решение:

Как вы хотите десериализовать в SubClassA ... Вам нужно использовать @JsonCreator в SubClassA ... Тогда вам нужно использовать @JsonIgnoreProperties, чтобы игнорировать свойства type, чтобы Джексон создал экземпляр SubClassA вместо ClassA ....

Попробуйте с ниже SubClassA :

@Getter
@JsonIgnoreProperties(ignoreUnknown = true)
public class SubClassA extends ClassA {
    private String dummyField;

    @JsonCreator
    public SubClassA(@JsonProperty("fields") final List<String> fields, @JsonProperty("mode") String mode,
                     @JsonProperty("dummyField") String dummyField) {
        super(fields, mode);
        this.dummyField = dummyField;
    }

}
...