Jackson fasterxml mapper - 序列化/反序列化接口/抽象对象类型列表的映射

时间:2017-03-10 08:55:13

标签: json serialization jackson deserialization objectmapper

我搜索过并搜索过,但没有找到答案/解决方案,所以我自己就这样问了。

使用fasterxml jackson 2.8.7

我有一个复杂的地图,我传递给angular并通过Spring映射作为JSON字符串返回。

地图:

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

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

我可以使用:

将其传递给棱角分明
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中[&#34; Table_A_List&#34;] - &GT; java.util.LinkedList中[8])

现在我的数据Map(tableEntryData)包含如下数据:

  

Map = {     [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,   },  ] }

这似乎是正确的。

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

的TableModel:

@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);
    }
}

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

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

TABLE_A_List中共有13个条目。

所以有人可以指出我如何正确地做到这一点。我想我需要更好地配置解串器或其他问题。

是的我总是可以回去摆脱TableModel方法并使用更好的东西,但这不是重点。此时需要进行太多的代码更改。我需要正确地完成这项工作。

1 个答案:

答案 0 :(得分:0)

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

使用:

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

而不是:

return mapper.readValue(jp, instanceClass);
readValue上的

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

感谢大家的回答! :*