1、背景
我们平台 依赖 一些平台的API能力,但是他们返回报文格式有点复杂,这次由于他们返回的是一个List,VO代表的是他们平台独有的对象,所以我们选择用List
[{"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行)。
def static getFirstOpportunityCardInfo(List<Map<String, Object>> opportunityCardList) {// 处理 opportunityCardListMap<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,说实话还是第一次听说这个。
map.put("cardId", item.get("fileds").get("cardId"));
JSONPath可以通过你的路径去匹配字段,如果匹配不到就会返回null,且不继续遍历寻找。
代码如下:
def static getFirstOpportunityCardInfo(Object opportunityCardList) {// 先转为List<JSONObject>List<JSONObject> opportunityCardListTemp = JSONArray.parseArray(JSON.toString(opportunityCardList), JSONObject.class);// 处理 opportunityCardListMap<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;
}
这样写就优雅很多了。
测试方法
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场景。