反序列化 JSon String int List<Map<String, Object>> 的正确方法
Proper way to Deserialize JSon String int List<Map<String, Object>>
我有一个 return 结果看起来像这样:
{"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”}]}
所以我正在调用休息服务,return 结果将始终以 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>(){});
我已经尝试了上述的各种配置,但我似乎无法在不引发无法识别的字段异常的情况下将其设置为 return。这是有道理的,因为键值中的第一个元素被视为映射器无法映射的字段,因为它未在我映射到的对象中的任何位置定义。
将此列表映射到对象的合适方法是什么?有没有一种简单的方法可以将值转换为适当的类型?我正在使用 jackson-databind,但如果有一个优雅的解决方案来解决这个问题,我可能会被说服使用 GSON。
嗯,显然它不起作用,因为杰克逊无法将 key/value 对映射到给定的地图。如果您希望 jackson 对此进行映射,请将 json 中的属性定义为 DTO 中的 class 字段。但这似乎违反直觉,因为您在给定的两个不同有效载荷中具有不同的属性,它们的结构不同。所以,这是你应该做的。让我们将每个对象表示为 json key/value 对与数组中对象的偏移值的映射。因此,这也将解决您的有效负载的动态特性。代码如下所示。
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}}
对于每个对象,key/value 映射是根据数组中该特定对象的相对偏移量打印的。如果需要,您可以尝试使用 Gson
,但这在我看来很简单。
从本质上讲,我指望杰克逊图书馆变得聪明。我需要遍历返回的 Json 的 'Tree structure'。将其映射到我的个人 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 块中。
我有一个 return 结果看起来像这样:
{"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”}]}
所以我正在调用休息服务,return 结果将始终以 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>(){});
我已经尝试了上述的各种配置,但我似乎无法在不引发无法识别的字段异常的情况下将其设置为 return。这是有道理的,因为键值中的第一个元素被视为映射器无法映射的字段,因为它未在我映射到的对象中的任何位置定义。
将此列表映射到对象的合适方法是什么?有没有一种简单的方法可以将值转换为适当的类型?我正在使用 jackson-databind,但如果有一个优雅的解决方案来解决这个问题,我可能会被说服使用 GSON。
嗯,显然它不起作用,因为杰克逊无法将 key/value 对映射到给定的地图。如果您希望 jackson 对此进行映射,请将 json 中的属性定义为 DTO 中的 class 字段。但这似乎违反直觉,因为您在给定的两个不同有效载荷中具有不同的属性,它们的结构不同。所以,这是你应该做的。让我们将每个对象表示为 json key/value 对与数组中对象的偏移值的映射。因此,这也将解决您的有效负载的动态特性。代码如下所示。
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}}
对于每个对象,key/value 映射是根据数组中该特定对象的相对偏移量打印的。如果需要,您可以尝试使用 Gson
,但这在我看来很简单。
从本质上讲,我指望杰克逊图书馆变得聪明。我需要遍历返回的 Json 的 'Tree structure'。将其映射到我的个人 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 块中。