Джексон не может прочитать поле, объявленное в суперклассе - PullRequest
0 голосов
/ 08 января 2020

У меня есть родительский класс:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "objectType")
@JsonSubTypes(
    {
        @JsonSubTypes.Type(value = Child.class, name = "child"),
    }
)
public class Parent {
    @JsonProperty("objectType")
    private ProductType productType

    public ProductType getProductType { return productType; }
}

Этот класс расширен дочерним классом:

public class Child extends Parent {
    @JsonProperty("field1")
    private Field1 field1;
    @JsonProperty("field2")
    private Field2 field2;
}

Я пытаюсь проанализировать следующий файл Json, представляющий сериализованный экземпляр Child класс:

{
    "objectType" : "child1",
    "field1" : "value1",
    "field2" : "value2"
}

... со следующим кодом:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
Parent parent = objectMapper.treeToValue(parentJsonNode, Parent.class);

(ps parentJsonNode - это JsonNode, созданный чтением файла JSON ).

В результате получается, что field1 и field2 моего объекта правильно прочитаны и установлены, однако поле objectType (объявленное в родительском классе) остается null после анализа.

Что нужно сделать, чтобы Джексон правильно прочитал значение child из objectType из объекта Json?

Вот код для ProductType:

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum ProductType {

    CHILD1("child1"), CHILD2("child2");

    private String objectType;

    private ProductType(String objectType) {
        this.objectType = objectType;
    }

    @JsonValue
    public String getObjectType() {  return objectType; }

}

1 Ответ

1 голос
/ 08 января 2020

Это связано со странным поведением Джексона добавить дополнительное поле, невидимое для вас во время компиляции, чтобы различать guish между подклассами, названными как значение, которое вы указали в @JsonTypeInfo. Поскольку ваш родительский класс содержит свойство, сериализованное в свойство JSON с именем objectType, а имя знаменателя, указанное в @JsonTypeInfo, также равно objectType. Если бы вы сериализовали объект Child, вы заметили бы, что сериализованный объект содержит два поля с одинаковым именем objectType, где будет заполнено только одно из них:

System.out.println(objectMapper.writeValueAsString(new Child()));

приводит к

{"objectType":"child","objectType":null,"field1":null,"field2":null}

Обходной путь - пропустить явное поле productType (сериализованное как objectType) в родительском классе, если оно не требуется в вашем коде, или назначить другое имя для сериализации (например, productType), но тогда вам, конечно, нужно иметь второе поле JSON, которое необходимо десериализовать в него:

Изменение

public class Parent {

    @JsonProperty("objectType")
    private String productType;

на

public class Parent {

    @JsonProperty("productType")
    private String productType;

и

Parent parent = objectMapper.treeToValue(objectMapper.readTree("{\n" +
    "    \"objectType\" : \"child\",\n" +
    "    \"productType\" : \"child\",\n" +
    "    \"field1\" : \"value1\",\n" +
    "    \"field2\" : \"value2\"\n" +
    "}"), Parent.class);
System.out.println(parent.getProductType());

приводит к выводу

child

Третья возможность - это явная инициализация поля, например, через конструктор, поскольку Джексон использует конструктор по умолчанию для создания объектов при десериализации:

// For subclasses
protected Parent (String productType) {
    this.productType = productType;
}

// For Jackson
public Parent () {
}

и

public Child () {
    super("child");
}

Редактировать: я использовал String вместо ProductType, чтобы упростить мой код, но результат похож.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...