如何优雅的处理解析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场景。
相关推荐
gywl1 小时前
openEuler VM虚拟机操作(期末考试)
linux·服务器·网络·windows·http·centos
某柚啊2 小时前
Windows开启IIS后依然出现http error 503.the service is unavailable
windows·http
_oP_i2 小时前
HTTP 请求Media typetext/plain application/json text/json区别
网络协议·http·json
码农君莫笑2 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
蜜獾云5 小时前
linux firewalld 命令详解
linux·运维·服务器·网络·windows·网络安全·firewalld
唐宋元明清21885 小时前
Windows 记录开机后应用启动慢的问题
windows·系统异常
fkdw6 小时前
C# Newtonsoft.Json 反序列化派生类数据丢失问题
c#·json
越甲八千7 小时前
重温设计模式--中介者模式
windows·设计模式·中介者模式
网络安全(华哥)9 小时前
X-Forwarded-For注入漏洞
windows·安全·web安全
styshoo10 小时前
wsl安装ubuntu 18.04
运维·windows·ubuntu