【问题标题】:Jackson fasterxml mapper - serialize/deserialize map of list of interface/abstract object typeJackson fasterxml 映射器 - 接口/抽象对象类型列表的序列化/反序列化映射
【发布时间】:2017-03-10 08:55:12
【问题描述】:

我已经搜索和搜索,但没有找到答案/解决方案,所以我自己问这个。

使用fastxml jackson 2.8.7

我有一个复杂的映射,我将它传递给 angular,然后通过 Spring 映射作为 JSON 字符串返回。

地图:

Map<String, List<TableModel>> tableEntryData = new LinkedHashMap<>();

它是 TableModel 列表的映射。表模型是一个接口,可以扩展到几个具体的类。因此,使用 TableModel 作为对象类型允许我将任何从 TableModel 扩展的具体类放入 List。

我可以使用以下方法将其传递给 Angular Fine:

ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
tableEntryJSON = mapper.writeValueAsString( tableEntryData );

但是当我尝试反序列化 JSON 时:

tableEntryData = mapper.readValue(tableEntryJSON, new TypeReference<LinkedHashMap<String, LinkedList<TableModel>>>() {} );

我正面临这个异常:

com.fasterxml.jackson.databind.JsonMappingException: (是 java.lang.NullPointerException) (通过引用链: java.util.LinkedHashMap["Table_A_List"]->java.util.LinkedList[8])

现在我的数据映射(tableEntryData)包含这样的数据:

地图 = { [Table_A_List] = {TableA 的 13 个对象的 LinkedList}, [Table_B_List] = {TableB 的 2 个对象的 LinkedList} }

其中 Table_A_List 和 Table_B_List 是映射键。 TableA 和 TabelB 是实现接口 TableModel 的类。

创建的 JSON 是这样的:

{   "TABLE_A_List" : [ {
    "deserializeType" : "TableA",
    "amount" : 0.0,   }, {
    "deserializeType" : "TableA",
    "amount" : 8.3,   }, {
    "deserializeType" : "TableA",
    "amount" : 20.0,   }, {
    "deserializeType" : "TableA",
    "amount" : 19.4,   }, {
    "deserializeType" : "TableA",
    "amount" : 33.9,   }, {
    "deserializeType" : "TableA",
    "amount" : 11.3,   }, {
    "deserializeType" : "TableA",
    "amount" : 23.6,   },{
    "deserializeType" : "TableA",
    "amount" : 2.6,   },{
    "deserializeType" : "TableA",
    "amount" : 3.6,   },{
    "deserializeType" : "TableA",
    "amount" : 23.0,   },{
    "deserializeType" : "TableA",
    "amount" : 230.6,   },{
    "deserializeType" : "TableA",
    "amount" : 23.8,   },{
    "deserializeType" : "TableA",
    "amount" : 11.1,   }, ],
"TABLE_B_List" : [ {
    "deserializeType" : "TableB",
    "code" : 9,   }, {
    "deserializeType" : "TableB",
    "code" : 1,   },  ] }

这似乎是正确的。

为了从接口正确反序列化具体类,我定义如下

表格模型:

@JsonDeserialize(using = ModelInstanceDeserializer.class) @JsonIgnoreProperties(ignoreUnknown = true) public interface TableModel { .... }

表A:

@JsonDeserialize(as = TableA.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public class TableA implements Serializable 
{
    String deserializeType = "TableA"; //This is for providing type info that helps with the deserialization since JSON mapper erases type info inside Maps/Lists

//public getters for fields
}

表B:

@JsonDeserialize(as = TableB.class)
@JsonIgnoreProperties(ignoreUnknown = true)
public class TableB implements Serializable 
{
    String deserializeType = "TableB"; //This is for providing type info that helps with the deserialization since JSON mapper erases type info inside Maps/Lists

//public getters for fields
}

然后我也创建了一个反序列化器类 ModelInstanceDeserializer:

import java.io.IOException;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class ModelInstanceDeserializer extends JsonDeserializer<TableModel> {
    @Override
    public TableModel deserialize(JsonParser jp, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {
        ObjectMapper mapper = (ObjectMapper) jp.getCodec();
        ObjectNode root = (ObjectNode) mapper.readTree(jp);
        Class<? extends TableModel> instanceClass = null;

        String type = "";
        if (root.has("deserializeType")) {
            type = root.get("deserializeType").asText();
        }

        if (type.equals("TableA")) {
            instanceClass = TableA.class;
        } else if (type.equals("TableA")) {
            instanceClass = TableB.class;
        }
        if (instanceClass == null) {
            return null;
        }
        return mapper.readValue(jp, instanceClass);
    }
}

现在虽然这个婴儿有点工作,但有一个问题。反序列化器将 第二个映射条目 读取为一个列表,该列表是第一个映射条目的一部分。正在发生的事情是,反序列化器似乎一次从 TABLE_A_List JSON 读取两条记录,结果在第 7 个条目中包含 TABLE_B_List:

{
  "TABLE_B_List" : [ {
    "deserializeType" : "TableB",
    "code" : 9,
  }, {
    "deserializeType" : "TableB",
    "code" : 1,
  },  ]
}

TABLE_A_List 共有 13 个条目。

所以任何人都可以指出我如何正确地做到这一点。我想我需要更好地配置反序列化器或其他问题。

是的,我总是可以返回并摆脱 TableModel 方法并使用更好的方法,但这不是重点。此时需要进行太多代码更改。我需要让这项工作正常进行。

【问题讨论】:

    标签: json serialization jackson deserialization objectmapper


    【解决方案1】:

    几分钟后找到了解决方案,尽管我为此失去了 2 天的大脑。

    用途:

    return mapper.readValue(root.toString(), instanceClass);
    

    代替:

    return mapper.readValue(jp, instanceClass);
    

    JsonParser 上的readValue 最终会增加根节点。这是一个逻辑错误+缺乏血腥指南。

    感谢大家的回答! :*

    【讨论】:

      猜你喜欢
      • 2011-06-28
      • 2015-04-10
      • 1970-01-01
      • 2018-02-28
      • 1970-01-01
      • 1970-01-01
      • 2017-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多