如何优雅的处理解析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场景。
相关推荐
YCOSA202535 分钟前
雨晨 Windows 10 企业版 LTSC x64 中度精简 1809 迎双节 17763.9257 特别版
windows
sukalot1 小时前
windows 驱动实例分析系列: HidHide 驱动分析 - drivers 篇(四)
windows·驱动开发
ι:6 小时前
NUC12 Pro 安装 Ubuntu 24.04 LTS 详细教程
linux·windows·ubuntu·nuc
YCOSA20256 小时前
微软宣布将于10月7日举行Windows与Surface发布会,聚焦“Windows、NVIDIA RTX Spark、Surface,以及更广泛PC生态”
windows
Source.Liu7 小时前
【A11】 labelprinter(Tauri 版)新建步骤
windows·rust
Punchline2788 小时前
解决 Windows 环境变量 Path 设置时提示“字符太长”的完整指南
windows·环境变量·path
数据狐(Datafox)9 小时前
1688商品列表API接口解析(附 JSON 样例)
java·开发语言·数据库·爬虫·json
chushiyunen9 小时前
claude code笔记(二)、claude桌面端(内网不好用)
windows
d_benhua9 小时前
以太网测试步骤
windows
冯一川9 小时前
DeepSeek在Windows系统上部署
windows·python