【问题标题】:Jackson deserialisation mappings to Java GenericsJackson 反序列化映射到 Java 泛型
【发布时间】:2015-04-10 17:42:02
【问题描述】:

我要做的是:给定一个 JSON 文档,使用 Jackson 将其映射到 POJO,但根据 JSON 文档中的字段定义 Generic 类成员的类型。

我的 JSON 如下所示

{
    "name": "Name",
    "parameters": [
                    {"name": "paramName","value": "Value1", "@type": "string"},
                    {"name": "size","value": 5,"@type": "double"}

                 ]
}

映射到这个 JSON 文档的类是

public class Strategy {
    public String name;
    public List<Parameter<?>> parameters;
}

然后我有一个通用类,如下所示

public class Parameter<T> {
    public String name;
    public T value;

    @Override
    public String toString() {
        return this.getClass().getName();
    }
}

所以想法是告诉Jackson当你将JSON文档反序列化为Strategy类并进入参数字段时,使用以下类作为值成员的Generic数据类型,即我想选择它为String或 Double 或 Integer,但我希望这是我的决定,以便它是通用的并且可以扩展到我想要的任何数据类型。

我意识到我可以像这样使用我添加的注释 JsonTypeInfo

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="@type")

但是按原样使用这些类实际上是可行的,但是 Jackson 根据其值自行决定类型应该是什么,并且我的 size 参数设置为 Integer。如果我将其值设置为 5.0,则将其设置为可以工作的 Double,但是如果我希望其中一个参数成为自定义对象怎么办?

我可以让它工作的唯一方法(并且对解决方案不是 100% 满意)是使 Parameter 类抽象,然后为我想要的每种类型创建具体类,即 ParameterString、ParameterDouble、ParameterCustomClass 然后使用 @JsonSubTypes 注解根据 JSON 文档中的类型字段设置要使用的正确类。

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="@type")
@JsonSubTypes({
    @JsonSubTypes.Type(value=ParameterString.class, name="string"),
    @JsonSubTypes.Type(value=ParameterDouble.class, name="double"),
    @JsonSubTypes.Type(value=ParameterInstrument.class, name="instrument")
})

以下面的类为例

public class StrategyParameterString extends StrategyParameter<String> { 

}

这不是很可扩展,我想它只需要为我需要的每种类型添加一个新的子类型注释和具体类,但感觉不像它应该的那样优雅。

有人知道更好的处理方法吗?

谢谢 安德鲁

【问题讨论】:

  • 我只是好奇你为什么要这样做。 Java 通过擦除实现泛型类型。这意味着泛型类型参数存在并且仅在编译时才有意义。它们在运行时不存在。您的List&lt;Parameter&lt;?&gt;&gt; 的运行时class 对象是List.class。如果您需要将类型信息与不可具体化的类型相关联,我建议您创建一个类型安全的异构容器并将其存储在您的 List 中,例如List&lt;MyHeterogenousContainer&gt;.
  • 感谢您的 cmets @scottb。基本上我通过最初为我需要的每种类型创建一个类,让它们实现一个接口并让我的 List 达到这一点,那里有两个问题,首先当我遍历列表时,对象不知道任何关于类成员,除非我强制转换它,其次每个类都有重复的代码,所以我将它重构为一个基本抽象类以减少代码重复。我想具体的类实现会在编译时设置类型,因此由于 Java 擦除,这可能是一种更好的处理方式。

标签: java json generics jackson


【解决方案1】:

据我了解,您想在Parameter 列表中表示的类型是可具体化的,例如。弦乐、双人舞、乐器。您可以利用可具体化类型在其类文字形式中具有运行时类型标记这一事实。这可以用来形成异构类型安全集合的基础。

不要以这种方式定义参数类:

public class Parameter<T> {
    public String name;
    public T value;
    :
    :
    }
}

您可以将其定义为将对象的值与其运行时类型标记相关联的具体类。

public class Parameter() {
    private final Object    m_value;
    private final Class<?>  m_cls;

    private Parameter(Class<?> token, Object val) {
        m_value = val;
        m_cls = token;
    }

    public static <T> Parameter newInstance(Class<T> token, T value) {
        return new Parameter(token, value);
    }
    :
    :

    public <T> T getValue(Class<T> token) {
        if (token != m_cls) throw new ClassCastException("Type error");
        return token.cast(m_value);
    }
}

在此设置中,类型标记和泛型方法(而不是泛型类型)用于设置和重新建立所需值的类型链接。您设置的值可以是任何类型,只要类型标记一致,就保证返回的类型与您存储的相同。

请注意,构造函数不能是泛型的。为了解决这个问题,已将 Parameter 的构造函数设为私有,并通过调用 newInstance() 静态工厂方法(可以是通用的)形成 Parameter 实例。

【讨论】:

  • 感谢@scottb 的详细解释,非常感谢。就像我理解你上面写的这个 Parameter 类一样,它有两个字段是值 (m_value) 和值代表的类 (m_cls) 这就是为什么 getValue 方法将值转换为正确的类类型?我想知道这是否适用于杰克逊反序列化?
  • Class 是可序列化的(并且 Class> 是可具体化的)。如果您项目中的其他引用类型也是可序列化的,我不明白为什么这不能工作。 getValue() 方法中的比较可能需要省略。问题是主机平台上的检查断言对象值的类型与所请求的类型相同。可能不适用于客户端。如果省略检查,则未检查强制转换;无法保证您请求的类型与存储的类型相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-10-18
  • 2012-07-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多