如何优雅的处理解析JSON数据

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场景。
相关推荐
Dolphin_Home28 分钟前
轻量实用的 XML 与 JSON / 对象互转工具类(Jackson 实现)
xml·java·json
女程序猿!!!1 小时前
视频分辨率
windows
不讲废话的小白1 小时前
文件拖不进企微了怎么办
windows·企微
聪明努力的积极向上2 小时前
【.net framework】WINDOWS服务和控制台程序简单介绍
windows·.net
程序员霸哥哥6 小时前
snipaste免费版下载安装使用教程(附安装包)
windows·microsoft·snipaste
程序员霸哥哥7 小时前
Keil5下载教程及安装教程(附安装包)
windows·keil5·keil5下载教程·keil5安装教程
AI大模型学徒7 小时前
Chatbox 安装 for Windows
windows·语言模型·chatgpt
llxxyy卢8 小时前
json的注入
json
千里马学框架9 小时前
windows系统上aosp15上winscope离线html如何使用?
android·windows·html·framework·安卓窗口系统·winscope
2501_938963969 小时前
Flutter 3.19 桌面应用开发:适配 Windows/macOS 端窗口大小与菜单栏自定义
windows·flutter·macos