杰克逊反序列化映射到Java泛型

时间:2015-02-10 15:51:17

标签: java json generics jackson

我想要做的是:给定一个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;
}

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

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

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

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

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

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

但是使用这些类实际上是可行的,但杰克逊根据其值确定类型应该是什么,并且我的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> { 

}

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

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

由于 安德鲁

1 个答案:

答案 0 :(得分:0)

据我了解,您希望在Parameter列表中表示的类型是可以恢复的,例如。字符串,双,乐器。您可以利用可再现类型以其类文字形式具有运行时类型标记的事实。这可以被用来形成异类型安全集合的基础。

而不是以这种方式定义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);
    }
}

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

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