如何使用具有类的通用类型的 ObjectMapper 反序列化 JsonString

Posted

技术标签:

【中文标题】如何使用具有类的通用类型的 ObjectMapper 反序列化 JsonString【英文标题】:How to deserialize JsonString using ObjectMapper with Generic Type of a class 【发布时间】:2019-03-17 11:54:56 【问题描述】:

假设我有一个模型类ResponseModel

@Setter // This one not working
public class ResponseModel<T> 

    private Class<T> responseClass;
    private String content; // JsonString

    public <T> T getContent() throws IOException  
        ObjectMapper mapper = new ObjectMapper();
        return mapper.readValue(content, responseClass);
    

我想要做的是我想将通用类型传递给类ResponseModel,当我调用方法getContent()时,它应该根据responseClass返回映射的对象

这是我想做的一个例子

 // Color Pojo
    @Data
    public class Color 

        private String nameValue;
        private String hexValue;

    

 // prepare mocked content
    final String content = "\n" +
            "\"nameValue\":\"red\",\n" +
            "\"hexValue\":\"FFFFFF\"\n" +
            "";

 // Declare ResponseModel Object
    ResponseModel<Color> response = new ResponseModel<>();
    response.setContent(content);
    response.getContent().getNameValue(); // should return red
    response.getContent().getHexValue(); // should return FFFFFF

有人知道怎么做吗?

【问题讨论】:

【参考方案1】:

您可以创建一个静态方法来反序列化具有泛型类型的对象

public class MyDeserializer 

    private static final ObjectMapper objectMapper = new ObjectMapper();

    public static  <T> T convertValue(String content, Class<T> contentClass) throws IOException 
        Assert.notNull(content, "Content cannot be null");
        Assert.notNull(contentClass, "Content class must be specified");
        return objectMapper.readValue(content, contentClass);
    

并测试您的方法:

Color color = MyDeserializer.convertValue("" +
            "\"nameValue\":\"red\"," +
            "\"hexValue\":\"FFFFFF\"" + "", Color.class);
assertEquals("red", color.getNameValue());
assertEquals("FFFFFF", color.getHexValue());

通过这种方式,您可以在运行时对任何类使用反序列化器。

更新

为了使您的示例正常工作,您需要在 getContent 之前删除以与类中的泛型类型 T 匹配。

public  T getContent() throws IOException 
    ObjectMapper mapper = new ObjectMapper();
    return  mapper.readValue(content, responseClass);

并使用您的方法:

responseModel.setContent("" +
         "\"nameValue\":\"red\"," +
         "\"hexValue\":\"FFFFFF\"" + "");
responseModel.setResponseClass(Color.class);

在运行时,泛型类型被替换为 Object,因此您必须指定预期的类。

我仍然认为第一个解决方案是干净的。您在问是否可以推导出 T 类,但在运行时看不到 T 的类型。

【讨论】:

我知道这个会起作用,但我想知道是否可以使用通用类型在一个我认为更方便的类中反序列化。 我添加了一个更新,如果您从外部设置 responseClass,您的解决方案将起作用。 我明白了。不幸的是,不可能做我想做的事。顺便说一句,非常感谢您的帮助。【参考方案2】:

可以用JsonSubTypes写一个接口自动反序列化如下:

@JsonIgnoreProperties(value = "type")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes(
        @JsonSubTypes.Type(value = Subclass1.class, name = "SUBCLASS1"),
        @JsonSubTypes.Type(value = Subclass2.class, name = "SUBCLASS2")
)
public interface DeserializableModelInterface 


并用这个特定的对象写你的ResponseModel

public class ResponseModel<T extends AbstractDeserializer> 

    private T body;

    public ResponseModel(T body) 
        this.body = body;
    

    public T getBody() 
        return body;
    

您的body 是一个特定的对象,您可以在运行时获得,而无需显式转换 json

在您的Subclass1Subclass2 等中,您将拥有一个额外的 json 属性,允许 jackson 自动反序列化/序列化

    
        "type": "SUBCLASS1"
        ... other properties
    

为避免手动映射子类值,您可以使用类名,例如:

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "class")

【讨论】:

也就是说当我们想使用一个类作为响应类的时候,每次都需要把这个类放到DeserializableModelInterface中? 我为每个映射指定了一个值,因为我想定义如何反序列化它。但这不是必需的。更新了答案并提供了更多详细信息 我明白了,但我认为对于我的情况,我不能在响应类中拥有额外的 json 属性。还有其他方法吗? 根据您的要求更新您的问题,这样人们就可以避免在您不需要的解决方案上浪费时间 推荐名字?

以上是关于如何使用具有类的通用类型的 ObjectMapper 反序列化 JsonString的主要内容,如果未能解决你的问题,请参考以下文章

如何在具有通用参数类型的接口中实现函数?

如何通过 AutoMapper 实现具有通用 Queue 类型属性的对象映射

C ++从具有嵌套类的接口派生

如何使用通用默认参数

是否可以在 Swift 中创建具有 Self 或关联类型要求的通用计算属性,如果可以,如何?

具有通用参数和抽象类的泛型