【问题标题】:Jackson Polymorphic Deserialization via field通过字段进行杰克逊多态反序列化
【发布时间】:2018-06-25 08:08:45
【问题描述】:

比方说,我有一堂课

public class A{
  private UUID typeId;
  private B data;
}

public abstract class B{
  private String a;
}

public class BChildOne extends B{
  ... some variables
}

public class BChildTwo  extends B{
  ... some variables
}

B类的类型在变化,根据A的typeId,所以如果A的typeId为“XXX”,则数据字段的类型为BChildOne,如果A的typeId为“YYY”,则数据字段的类型为BChildTwo。

我怎样才能做到这一点?

所以我试过了;

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility =
JsonAutoDetect.Visibility.NONE, setterVisibility = 
JsonAutoDetect.Visibility.NONE)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = 
JsonTypeInfo.As.EXTERNAL_PROPERTY , property = "typeId")
@JsonSubTypes({
 @JsonSubTypes.Type(value = BChildOne.class, name = "40ad2fe6-e672-4f0e- 
986e- 
 619c7a1a3223") }
 )
 public abstract class B{

但我收到以下错误;

意外的令牌 (END_OBJECT),预期的 FIELD_NAME:缺少包含类型 ID 的属性“typeId”(对于 B 类)

这很明显,因为 typeId 字段属于 A 类而不是 B。

【问题讨论】:

  • 有必要用Jackson做这个多态执行吗?我猜你可以手动完成。如果它具有非常酷的开箱即用多态功能,我没有更深入的杰克逊知识
  • 扩展B类的子类可以超过一百个。这就是我尝试实施通用解决方案的原因。

标签: java jackson deserialization fasterxml


【解决方案1】:

假设你的 JSON 文档是这样的:

{
  "type": "foo",
  "data": {
    "someCommonProperty": "common property",
    "fooProperty": "foo specific property"
  }
}
{
  "type": "bar",
  "data": {
    "someCommonProperty": "common property",
    "barProperty": "bar specific property"
  }
}

你可以使用:

public class Wrapper {

    private String type;

    @JsonTypeInfo(use = Id.NAME, property = "type", include = As.EXTERNAL_PROPERTY)
    @JsonSubTypes(value = { 
        @JsonSubTypes.Type(value = Foo.class, name = "foo"),
        @JsonSubTypes.Type(value = Bar.class, name = "bar") 
    })
    private AbstractData data;

    // Getters and setters
}
public abstract class AbstractData {

    private String someCommonProperty;

    // Getters and setters
}
public class Foo extends AbstractData {

    private String fooProperty;

    // Getters and setters
}
public class Bar extends AbstractData {

    private String barProperty;

    // Getters and setters
}

在这种方法中,@JsonTypeInfo 设置为使用type 作为external property 来确定映射data 属性的正确类。 JSON文档可以反序列化如下:

ObjectMapper mapper = new ObjectMapper();
Wrapper wrapper = mapper.readValue(json, Wrapper.class);  

【讨论】:

    【解决方案2】:

    我曾经遇到过同样的序列化问题,我做了一个这样的实现。请看下面的代码。

        protected Entity serialize(Object entity) throws Exception {
        try {
            if ( entity instanceof AbstractProcessRequest ) {
                AbstractProcessRequest request = (AbstractProcessRequest) entity;
                String value = mapper.writeValueAsString(request.prepareJSONRequest());
                logger.info("Telesales String json request " + value);
                return new Entity(value, UTF_8);
            } else {
                String value = mapper.writeValueAsString(entity);
                return new StringEntity(value, UTF_8);
            }
        } catch (Exception e) {
            logger.error("Telesales --> Error occured serializing entity", e);
            throw e;
        }
    }
    

    为了实现通用结构,在一些服务类中创建了一个执行方法,如下所示。

    private <T> T execute(Object entity, Class<T> clazz, HttpRequestBase request, String contentType) throws Exception {
    

    【讨论】:

    • 根据这个解决方案,我应该为 B 的所有孩子添加带有“instance of”的 else if 语句,对吧?
    • 在我的情况下,实际上不会有数百个实例。是的,这似乎是一个问题。您可以在枚举或某些属性文件等中包含这些实例名称,但这不是一个很酷的解决方案。
    • 很遗憾,我的客户不会接受它作为解决方案,不过还是感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-05-13
    • 2015-08-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多