如何优雅的处理解析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场景。
相关推荐
Clank的游戏栈1 小时前
Unity多线程渲染指令队列设计与集成技术详解
windows·unity·游戏引擎
sukida1006 小时前
BIOS主板(非UEFI)安装fedora42的方法
linux·windows·fedora
界面开发小八哥8 小时前
界面控件DevExpress WPF v25.1预览 - 支持Windows 11系统强调色
windows·wpf·界面控件·devexpress·ui开发·.net 9
岫珩12 小时前
“由于启动计算机时出现了页面文件配置问题,Windows在你的计算机上创建了一个临时页面文件。。。”的问题解决
windows
李菠菜12 小时前
解决Windows系统下Git克隆时报错“unable to checkout working tree”的方法详解
windows·git
沉迷...14 小时前
详解.vscode 下的json .vscode文件夹下各个文件的作用
ide·vscode·json
子非衣16 小时前
Windows云主机远程连接提示“出现了内部错误”
服务器·windows
剁椒排骨18 小时前
win11什么都不动之后一段时间黑屏桌面无法显示,但鼠标仍可移动,得要熄屏之后才能进入的四种解决方法
运维·windows·经验分享·计算机外设·win11·win10
李菠菜18 小时前
Windows Terminal 集成 Git Bash 的简洁配置指南
windows·git
大数据魔法师19 小时前
Hadoop生态圈框架部署 - Windows上部署Hadoop
大数据·hadoop·windows