【问题标题】:Does Jackson deserialization have a maximum for inhertiance depth?Jackson 反序列化是否有最大继承深度?
【发布时间】:2019-06-24 09:46:01
【问题描述】:

换句话说,可以实现的继承深度是否存在限制。 目前我的深度为2,祖父母->父母->孩子,我遇到了一个问题,杰克逊可以反序列化到父母然后抛出UnrecognizedPropertyException。这是正确的,但是子类确实拥有该属性,我相信我已经为杰克逊添加了正确的类型信息来反序列化子类。

此测试显示问题:

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonPOJOBuilder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Value;
import lombok.experimental.SuperBuilder;
import org.junit.Assert;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

public class JacksonInheritanceTest {

    @Test
    public void deserializeChildrenAsGrandParentList() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        String grandparentsJson = "{" +
                "\"list\":[{" +
                "\"type\": \"parent\"," +
                "\"value\": \"child\"," +
                "\"someProperty\": \"foobar\"" +
                "}]" +
                "}";
        GrandParentList grandparents = mapper.readValue(grandparentsJson, GrandParentList.class);
        Assert.assertNotNull(grandparents);
    }

    @Test
    public void deserializeParentAsGrandParent() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        String parentJson = "{" +
                "\"type\": \"parent\"," +
                "\"value\": \"child\"" +
                "}";
        GrandParent grandparent = mapper.readValue(parentJson, GrandParent.class);
        Assert.assertNotNull(grandparent);
    }

    @Test
    public void deserializeChildAsGrandParent() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        String grandparentJson = "{" +
                "\"type\": \"parent\"," +
                "\"value\": \"child\"," +
                "\"someProperty\": \"foobar\"" +
                "}";
        GrandParent grandparent = mapper.readValue(grandparentJson, GrandParent.class);
        Assert.assertNotNull(grandparent);
    }

    @Test
    public void deserializeChildAsParent() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        String childJson = "{" +
                "\"type\": \"parent\"," +
                "\"value\": \"child\"," +
                "\"someProperty\": \"foobar\"" +
                "}";
        Parent parent = mapper.readValue(childJson, Parent.class);
        Assert.assertNotNull(parent);
    }

    @Test
    public void deserializeAsChild() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        String child1 = "{" +
                "\"type\": \"parent\"," +
                "\"value\": \"child\"," +
                "\"someProperty\": \"foobar\"" +
                "}";
        Child child = mapper.readValue(child1, Child.class);
        Assert.assertNotNull(child);
    }
}

class GrandParentList {
    @JsonProperty
    List<GrandParent> list;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Parent.class,
                name = "parent")
})
@Getter
@SuperBuilder
@JsonDeserialize(builder = GrandParent.GrandParentBuilderImpl.class)
class GrandParent {
    @JsonProperty("type")
    private String type;

    @JsonPOJOBuilder(withPrefix = "")
    static final class GrandParentBuilderImpl extends GrandParentBuilder<GrandParent, GrandParent.GrandParentBuilderImpl> {
    }
}


@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "value", visible = true)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Child.class, name = "child")
})
@Getter
@SuperBuilder
@JsonDeserialize(builder = Parent.ParentBuilderImpl.class)
class Parent extends GrandParent {
    @JsonProperty
    private String value;

    @JsonPOJOBuilder(withPrefix = "")
    static final class ParentBuilderImpl extends ParentBuilder<Parent, ParentBuilderImpl> {
    }
}


@EqualsAndHashCode(callSuper = true)
@Value
@SuperBuilder
@JsonDeserialize(builder = Child.ChildBuilderImpl.class)
class Child extends Parent {
    @JsonProperty
    private String someProperty;

    @JsonPOJOBuilder(withPrefix = "")
    static final class ChildBuilderImpl extends ChildBuilder<Child, ChildBuilderImpl> {
    }
}

【问题讨论】:

  • 如果不做超过 2 个就会有问题,如果超过 4 个就会有问题。
  • 这个例子是完整的,但不是最小的。到目前为止我知道没有任何限制,在我看来,您没有将“孩子”定义为祖父母或父母的子类,并且孩子没有用 jsontype 进行注释。更多 jsonTypeName 示例:stackoverflow.com/questions/56395909/…

标签: java json jackson


【解决方案1】:

以下是如何定义具有多个级别的继承:

您想反序列化最终类型为“child”的 GrandParent 列表

  {
      "list":[{
          "type": "child",
          "someProperty": "foobar"
      }]
  }

继承树是:

GrandParent
  Parent
    Child(someProperty:String)

你必须在顶层定义你的“类型”属性,@JsonTypeInfo(...) 您可以在子级别上重复它,但如果您只序列化/反序列化祖父母,则不需要。 然后在每个父级别(Parent 和 GrandParent 类)上定义子类型,就像使用 @JsonSubTypes 一样。

代码

public class JacksonInheritanceTest2 {

    @Test
    public void deserializeChildrenAsGrandParentList() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        String grandparentsJson = "{" +
                "\"list\":[{" +
                "\"type\": \"child\"," +
                "\"someProperty\": \"foobar\"" +
                "}]" +
                "}";
        GrandParentList grandparents = mapper.readValue(grandparentsJson, GrandParentList.class);
        Assertions.assertNotNull(grandparents);
    }


}

class GrandParentList {
    @JsonProperty
    List<GrandParent> list;
}

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Parent.class,name = "parent"),
        //@JsonSubTypes.Type(value = Child.class, name = "child")
})
class GrandParent {
    @JsonProperty("type")
    private String type;

}


//@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
@JsonSubTypes({
        @JsonSubTypes.Type(value = Child.class, name = "child")
})
class Parent extends GrandParent {
    @JsonProperty
    private String value;

}

@JsonSubTypes({
    @JsonSubTypes.Type(value = Child.class, name = "child")
})
class Child extends Parent {
    @JsonProperty
    private String someProperty;

    public String getSomeProperty() {
        return someProperty;
    }

    public void setSomeProperty(String someProperty) {
        this.someProperty = someProperty;
    }



}

你犯的错误:

  • 定义多个属性类型名称,每个用于一个父级别:您选择属性类型名称并仅使用一个。
  • 在 Json 中,您确实在摘要中设置了父级的类型名称:只有叶子类型很重要,树的其余部分被扣除。

侧节点:Assertions 来自junit5,它与来自junit4 的Assert 相同

【讨论】:

  • 所以如果我理解正确的话。开箱即用的 Jackson 只能处理基于一个属性名称的类型推断?我的问题是我需要基于两个属性而不是一个属性进行类型推断。我假设在不同的继承深度添加@JsonTypeInfo 将允许杰克逊推断出哪个属性将映射到哪个深度并将它们链接在一起。不幸的是,我似乎需要为此编写一些自定义的东西。感谢您的回复!
  • @ByeBye 没有类型推断按预期工作,您的对象一次只有一种类型。您不能为一个对象指定 2 种类型(您将如何处理 java 代码?)
  • @ByeBy 也许我可以解释自省的工作方式:1. 你给祖父母类及其配置 2. Jackson 找到类上的注释-> 获取对象的类型名称-然后扫描递归地找到与该名称关联的真实类 3. Jackson 实例化该类(不需要知道此级别的 Parent 中间类) 3. 它会影响找到的所有字段给 Child 类-再次知道 Parent 的所有字段通过继承 - 不需要指定父级。在所有这些 3 步中,您永远不需要指定“孩子”的实例是“父母”
  • 是的,我就是这个意思。我只能在注释中指定一次类型信息(在GrandParent 上),并且我只能使用一个属性进行类型推断(type 属性)。我会把你的答案标记为答案。谢谢。
【解决方案2】:

在 ObjectMapper 对象上设置配置:

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

【讨论】:

  • 不幸的是,这不是我的问题的答案。因为这将导致杰克逊简单地忽略ChildsomeProperty 属性并返回Parent 的实例而不是Child。这不是我所期望的行为。
猜你喜欢
  • 1970-01-01
  • 2020-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-21
  • 2020-11-21
  • 1970-01-01
相关资源
最近更新 更多