1、背景
我们平台 依赖 一些平台的API能力,但是他们返回报文格式有点复杂,这次由于他们返回的是一个List,VO代表的是他们平台独有的对象,所以我们选择用List来接收的,但是无法被正常的解析出来,后来研究后,和预期格式不太一样,我预期的是应该没有type,fields这些字段的,直接就可以取到值的。
json
[
{
"type": "com.xxx.xxxx.xxxxx.facade.response.XXXXXX",
"fields": {
"cardId": "123456789",
"feedbackList": [
{
"type": "com.xxx.xxxx.xxxxx.facade.response.XXXXXXRequest",
"fields": {
"closeOpportunity": true,
"optionDesc": "关闭"
}
},
{
"type": "com.xxx.xxxx.xxxxx.facade.response.XXXXXXFeedbackOption",
"fields": {
"closeOpportunity": false,
"optionDesc": "不关闭"
}
}
],
"sceneName": "测试场景-test"
}
}
]
错误代码
由于我的预期是没有type,fileds字段,所以在取值的时候就出现了NullPointerException(第12行)。
groovy
def static getFirstOpportunityCardInfo(List<Map<String, Object>> opportunityCardList) {
// 处理 opportunityCardList
Map<String, Object> firstOpportunityCard = new HashMap<>();
if (Objects.nonNull(opportunityCardList) && opportunityCardList.size() > 0) {
def opportunityCards = new ArrayList<Map<String, String>>();
def sceneNames = new ArrayList<String>();
// 取第一个
Map<String, String> item = opportunityCardList.get(0);
Map<String, String> map = new HashMap<String, String>();
map.put("cardId", item.get("cardId"));
JsonSlurper jsonSlurper = new JsonSlurper();
def feedbackOptions = jsonSlurper.parseText(item.get("feedbackList"));
String feedback = feedbackOptions[0].get("optionDesc");
map.put("feedback", feedback);
opportunityCards.add(map);
sceneNames.add(item.get("sceneName"));
firstOpportunityCard.put("opportunityCards", opportunityCards);
firstOpportunityCard.put("sceneNames", sceneNames);
}
return firstOpportunityCard;
}
2、解决方案
好了,问题原因已经定位到了,原因就是返回的报文和预期不一致导致的。
如果按照原来的思路也可以实现,就是按照如下思路一个一个往下套,这样很麻烦也不优雅,而且还会有NullPointerException的case,所以不推荐,这里蚂蚁大佬(P7)给了一个方案就是使用JSONPath,说实话还是第一次听说这个。
groovy
map.put("cardId", item.get("fileds").get("cardId"));
JSONPath可以通过你的路径去匹配字段,如果匹配不到就会返回null,且不继续遍历寻找。
代码如下:
groovy
def static getFirstOpportunityCardInfo(Object opportunityCardList) {
// 先转为List<JSONObject>
List<JSONObject> opportunityCardListTemp = JSONArray.parseArray(JSON.toString(opportunityCardList), JSONObject.class);
// 处理 opportunityCardList
Map<String, Object> firstOpportunityCard = new HashMap<>();
if (Objects.nonNull(opportunityCardListTemp) && opportunityCardListTemp.size() > 0) {
JSONObject item = opportunityCardListTemp.get(0);
String cardId = JSONPath.eval(item, '$.fields.cardId');
String optionDesc = JSONPath.eval(item, '$.fields.feedbackList[0].fields.optionDesc');
String sceneName = JSONPath.eval(item, '$.fields.sceneName');
// 这里是业务逻辑,可以不看,只看上面取值部分即可
def opportunityCards = new ArrayList<Map<String, String>>();
def sceneNames = new ArrayList<String>();
Map<String, String> map = new HashMap<String, String>();
map.put("cardId", cardId);
map.put("feedback", optionDesc);
opportunityCards.add(map);
sceneNames.add(sceneName);
firstOpportunityCard.put("opportunityCards", opportunityCards);
firstOpportunityCard.put("sceneNames", sceneNames);
}
return firstOpportunityCard;
}
这样写就优雅很多了。
测试方法
groovy
static void main(String[] args) {
String str = "[\n" +
" {\n" +
" \"type\": \"com.xxx.xxxx.xxxxx.facade.response.XXXXXX\",\n" +
" \"fields\": {\n" +
" \"cardId\": \"123456789\",\n" +
" \"feedbackList\": [\n" +
" {\n" +
" \"type\": \"com.xxx.xxxx.xxxxx.facade.response.XXXXXXRequest\",\n" +
" \"fields\": {\n" +
" \"closeOpportunity\": true,\n" +
" \"optionDesc\": \"关闭\"\n" +
" }\n" +
" },\n" +
" {\n" +
" \"type\": \"com.xxx.xxxx.xxxxx.facade.response.XXXXXXFeedbackOption\",\n" +
" \"fields\": {\n" +
" \"closeOpportunity\": false,\n" +
" \"optionDesc\": \"不关闭\"\n" +
" }\n" +
" }\n" +
" ],\n" +
" \"sceneName\": \"测试场景-test\"\n" +
" }\n" +
" }\n" +
"]";
def info = getFirstOpportunityCardInfo(JSONArray.parseArray(str));
println(info)
}
3、适用场景
- JSONPath: 适合数据结构复杂或层次较深的JSON,如果不想要手动处理处理每个层级,或者想要在一个复杂的JSON文档中进行灵活的查询,JSONPath更方便。同时,JSONPath支持条件过滤(例如查询所有满足特定条件的元素)。
- JSONObject和Map:适合于数据结构简单、固定的JSON场景。