如何优雅的处理解析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场景。
相关推荐
一条晒干的咸魚2 分钟前
【Web前端】创建我的第一个 Web 表单
服务器·前端·javascript·json·对象·表单
系统之家装机大师1 小时前
Win11 22H2/23H2系统11月可选更新KB5046732发布!
windows·电脑
系统之家装机大师1 小时前
微软发布Win11 24H2系统11月可选更新KB5046740!
windows·电脑
戎梓漩3 小时前
windows下安装curl,并集成到visual studio
ide·windows·visual studio
蓝田~5 小时前
观察者模式和订阅模式
windows·观察者模式
梓仁沐白12 小时前
ubuntu+windows双系统切换后蓝牙设备无法连接
windows·ubuntu
九鼎科技-Leo16 小时前
什么是 WPF 中的依赖属性?有什么作用?
windows·c#·.net·wpf
Yang.9918 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
我不瘦但很逗18 小时前
Windows下使用DBeaver连接云数据库(MySQL)
数据库·windows
ashane131419 小时前
Java list
java·windows·list