【问题标题】:Proper way to Deserialize JSon String int List<Map<String, Object>>反序列化 JSon String int List<Map<String, Object>> 的正确方法
【发布时间】:2018-09-10 00:24:50
【问题描述】:

我的返回结果可能如下所示:

{"rows":[{"ProfileID":"SLappE","CR_ProfileID":"BC1A"},{"ProfileID":"SCRA","CR_ProfileID":"BC15"},{"ProfileID":"SMOKE","CR_ProfileID":"BC15"},{"ProfileID":"Smokey","CR_ProfileID":"BC1F"},{"ProfileID":"WF-LN-OCR","CR_ProfileID":"BC1F"}]}

或者像这样:

{"rows":[{"PARAM":"product_reference_nr","FIELDNAME":"account_nr","EXAMPLE-FIELD":"CHK","EXAMPLE-TABLE":"CHK_DOC","ID":"CHK-ACCT"},{"PARAM":"party_pd_nr","FIELDNAME":"front_image_object_id","EXAMPLE-FIELD":"CHK","EXAMPLE-TABLE":"BACK_CHK_DOC","ID":"CHK-BACK"},{"PARAM":"endDate","FIELDNAME":"document_dt","EXAMPLE-FIELD":"HR","EXAMPLE-TABLE":"SPLAPSTIC_DOC","ID":"HR-DT"},{"PARAM":"startDate","FIELDNAME":"document_dt","EXAMPLE-FIELD":"HR","EXAMPLE-TABLE":"SPLAPSTIC_DOC","ID":"HR-DT-STARTDT"},{"PARAM":"formCode","FIELDNAME":"form_category_cd","EXAMPLE-FIELD":"HR","EXAMPLE-TABLE":"SPLAPSTIC_DOC","ID":"HR-FORM”}]}

所以我调用了一个 rest 服务,返回结果总是以 rows: 开头,然后是一个看起来是 Dictionary 或 Map/HaspMap 值的数组。该字典的大小会有所不同。但在该行列表中,它似乎是键值对。

现在我尝试将该 json 反序列化为这个数据结构:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;

import javax.xml.bind.annotation.XmlRootElement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement
public class EResponse {

    @JsonProperty("rows")
    private final List<Row> rows = new ArrayList<>();

    public List<Row> getRows() {
        List<Row> retResult = rows;
        return retResult;
    }
}

import com.fasterxml.jackson.annotation.*;

import javax.xml.bind.annotation.XmlRootElement;
import java.util.Hashtable;
import java.util.Map;

@JsonInclude(JsonInclude.Include.NON_NULL)
@XmlRootElement
public class Row {
    private Map<String,Object> column;

    @JsonProperty
    public Map<String, Object> getColumn(){
        return column;
    }
}

我这样称呼上面的代码:

result = mapper.readValue(response.readEntity(String.class), new TypeReference<EResponse>(){});

我已经尝试了上述的各种配置,但如果不引发 Unrecognized field 异常,我似乎无法让它返回。这是有道理的,因为 Key 值中的第一个元素被视为映射器无法映射的字段,因为它没有在我映射到的对象中的任何地方定义。
将此 List> 映射到对象的适当方法是什么?有没有一种简单的方法可以将值转换为适当的类型?我正在使用 jackson-databind,但如果有一个优雅的解决方案可以解决这个问题,我可以说服他们使用 GSON。

【问题讨论】:

  • “键值中的第一个元素被视为一个字段”——您的意思是键值“行”?如果是这样,您总是可以在收到响应字符串后立即编辑它并删除前 9 个字符(和最后 2 个字符)并照常进行。我可以尝试提供一个答案,但我不确定我是否理解这个问题,或者你为什么要处理两种不同类型的回复......或者你为什么说“似乎包含”之类的话,就好像你没有设计自己回应。
  • 我的意思是'ProfileID'。它定义了行就好了。如果我想获得更具体的信息并映射其余的响应,那就是当它变得棘手时。响应 JSON 来自特定供应商。每次您拨打电话时都可以使用的对象。这实际上取决于您如何设置请求。我想将此功能放入一个 jar 中,我可以将其用作 REST 服务的通用客户端

标签: java json java-8 json-deserialization jackson-databind


【解决方案1】:

基本上,我指望杰克逊图书馆变得聪明。我需要遍历返回的 Json 的“树结构”。将其映射到我的个人 Response 对象。有点像这样:

            ObjectMapper mapper = new ObjectMapper();
        JsonNode rootNode = mapper.readTree(response.readEntity(String.class));
        JsonNode rowsNode = rootNode.path("rows");
        Iterator<JsonNode> elements = rowsNode.elements();
        while (elements.hasNext()){
            JsonNode columnNodes = elements.next();
            System.out.println("All the Column Values: " + columnNodes.asText());
            Map<String, Object> values = new HashMap<String, Object>();
            values = mapper.treeToValue(columnNodes, HashMap.class);
            ERow row = new ERow();
            row.setColumn(values);
            columns.add(row);
        }
        result.setRows(columns);

别担心,我将它包装在一个 try catch 块中。

【讨论】:

    【解决方案2】:

    好吧,显然它不起作用,因为杰克逊无法将键/值对映射到给定的映射。如果您希望杰克逊对此进行映射,请将 json 中的属性定义为 DTO 中的类字段。但这似乎违反直觉,因为您在给定的两个不同有效负载中具有不同的属性,它们的结构是不同的。所以,这是你应该做的。让我们将每个对象表示为 json 键/值对与数组中对象的偏移值的映射。因此,这也将解决您的有效负载的动态性质。代码如下所示。

    final String jsonArray = new String(Files.readAllBytes(Paths.get("./src/main/resources/payload.json")));
    
    final TypeReference<Map<String, String>> typeRef = new TypeReference<Map<String, String>>() {
    };
    Iterator<Map<String, String>> it = new ObjectMapper().readerFor(typeRef)
            .readValues(JsonPath.parse(jsonArray).read("rows").toString());
    final AtomicInteger counter = new AtomicInteger();
    Map<Integer, Map<String, String>> mapByObject = new HashMap<>();
    it.forEachRemaining(keyValueMap -> {
        mapByObject.putIfAbsent(counter.incrementAndGet(), keyValueMap);
    });
    System.out.println(mapByObject);
    

    输出是这样的:

    {1={ProfileID=SLappE, CR_ProfileID=BC1A}, 2={ProfileID=SCRA, CR_ProfileID=BC15}, 3={ProfileID=SMOKE, CR_ProfileID=BC15}, 4={ProfileID=Smokey, CR_ProfileID=BC1F}, 5={ProfileID=WF-LN-OCR, CR_ProfileID=BC1F}}
    

    对于每个对象,键/值映射是针对该特定对象在数组中的相对偏移量打印的。如果需要,您可以尝试使用Gson,但这很简单,IMO。

    【讨论】:

    • 谢谢兰瓦拉。这是一个很好的答案,但我正在寻找一种将响应映射回我的对象​​的方法。我对你的回答投了赞成票。
    猜你喜欢
    • 2018-09-22
    • 2018-01-09
    • 2021-09-07
    • 2020-08-01
    • 1970-01-01
    • 2014-05-19
    • 2020-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多