java使用jackson解析复杂json字符串

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Mrqiang9001/article/details/82872405

为什么要用 jackson

jackson 凭借其简洁的语法、高效的处理速度、丰富的功能、清晰的文档等众多优势,受到广大开发者的热爱,成为了java程序员处理json数据的不二选择。

jackson 的官方文档:
https://www.baeldung.com/jackson

1 实战需求

在电商项目中,订单物流查询模块,需要接入多方快递 API 查询接口,每一家 API 接口返回的数据格式、字段都不一样,要求将多方 API 接口查询结果整合成一套,无论使用哪一家的接口,返回的结果、字段都是一样的,方便客户端的调用。

某快递查询接口返回的json格式物流信息:

{
  "resultcode": "200", /* 老版状态码,新用户请忽略此字段 */
  "reason": "查询物流信息成功",
  "result": {
    "company": "EMS", /* 快递公司名字 */
    "com": "ems",
    "no": "1186465887499", /* 快递单号 */
    "status": "1", /* 1表示此快递单的物流信息不会发生变化,此时您可缓存下来;0表示有变化的可能性 */
    "list": [
      {
        "datetime": "2016-06-15 21:44:04",  /* 物流事件发生的时间 */
        "remark": "离开郴州市 发往长沙市【郴州市】", /* 物流事件的描述 */
        "zone": "" /* 快件当时所在区域,由于快递公司升级,现大多数快递不提供此信息 */
      }/** 此处省略部分物流踪迹 **/
     {
        "datetime": "2016-06-20 17:55:00",
        "remark": "投递并签收,签收人:单位收发章 *【毕节地区】",
        "zone": ""
      }
    ]
  },
  "error_code": 0 /* 错误码,0表示查询正常,其他表示查询不到物流信息或发生了其他错误 */
}

2 技术分析

在查看多家快递查询API接口文档之后,作者发现快递物流信息一般都可以采用 json 格式作为数据传输格式,而且返回的 json 数据存在嵌套的现象(快递的物流轨迹属于嵌套的,而且是数组)。作者在jackson的官方文档中并没有发现直接处理复杂json格式的示例。因此需要自行参考 API 文档,手动编写解析复杂json的方法。

3 作手解决

3.1 编写「快递信息封装类」

快递物流轨迹类: LogisticsTrace

package com.ljq.demo.bean;

import lombok.Data;

import java.io.Serializable;

/**
 * @Description: 快递物流轨迹信息
 * @Author: junqiang.lu
 * @Date: 2018/9/26
 */
@Data
public class LogisticsTrace implements Serializable {

    private static final long serialVersionUID = -5152487306550447774L;

    /**
     * 接收时间
     */
    private String acceptTime;

    /**
     * 物流描述
     */
    private String acceptStation;

    /**
     * 备注信息
     */
    private String remark;
}

物流信息类: LogisticsInfo

package com.ljq.demo.bean;

import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
 * @Description: 物流信息 model
 * @Author: junqiang.lu
 * @Date: 2018/9/26
 */
@Data
public class LogisticsInfo implements Serializable {

    private static final long serialVersionUID = 6951873346591540974L;

    /**
     * 快递公司识别码
     */
    private String comCode;

    /**
     * 快递单号
     */
    private String postNo;

    /**
     * 快递物流信息查询是否成功
     */
    private boolean success;

    /**
     * 快递查询失败原因
     */
    private String failReason;

    /**
     * 快递物流状态
     */
    private int state;

    /**
     * 快递物流轨迹信息
     */
    List<LogisticsTrace> logisticsTraceList;
}

3.2 使用 jackson 解析复杂 json 格式数据

jackson maven 引用:
jackson 版本使用最新的即可,作者这里使用的是 2.9.6 版本

 <!-- json -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${jackson}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${jackson}</version>
    </dependency>

物流信息 json 格式转换工具类: LogisticsUtils

package com.ljq.demo.util;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ljq.demo.bean.LogisticsInfo;
import com.ljq.demo.bean.LogisticsTrace;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: 物流信息工具类
 * @Author: junqiang.lu
 * @Date: 2018/9/26
 */
public final class LogisticsUtils {

    /**
     * 解析通过「A」接口提供方获取的物流信息
     * json 解析: jackson, https://www.baeldung.com/jackson
     *
     * @param jsonStr 物流信息,json 格式字符串
     * @return 封装后的物流信息
     */
    public static LogisticsInfo getLogisticsInfoByA(String jsonStr) throws IOException {

        LogisticsInfo logisticsInfo = new LogisticsInfo();
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = objectMapper.readTree(jsonStr);
        String comCode = rootNode.get("comCodeA") != null ? rootNode.get("comCodeA").asText() : "";
        String postNo = rootNode.get("postNoA") != null ? rootNode.get("postNoA").asText() : "";
        boolean success = rootNode.get("successA") != null ? rootNode.get("successA").asBoolean() : false;
        String failReason = rootNode.get("failReasonA") != null ? rootNode.get("failReasonA").asText() : "";
        int state = rootNode.get("stateA") != null ? rootNode.get("stateA").asInt() : 0;

        logisticsInfo.setComCode(comCode);
        logisticsInfo.setPostNo(postNo);
        logisticsInfo.setSuccess(success);
        logisticsInfo.setFailReason(failReason);
        logisticsInfo.setState(state);
        /**
         * 遍历物流轨迹
         */
        if (rootNode.get("tracesA") != null && rootNode.get("tracesA").size() > 0) {
            List<LogisticsTrace> logisticsTraceList = new ArrayList<>(16);
            LogisticsTrace logisticsTrace;
            JsonNode traceNode = rootNode.get("tracesA");
            for (int i = 0; i < traceNode.size(); i++) {
                logisticsTrace = new LogisticsTrace();
                logisticsTrace.setAcceptTime(traceNode.get(i).get("acceptTimeA").asText());
                logisticsTrace.setAcceptStation(traceNode.get(i).get("acceptStationA").asText());
                logisticsTraceList.add(logisticsTrace);
            }
            logisticsInfo.setLogisticsTraceList(logisticsTraceList);
        }
        return logisticsInfo;
    }

    /**
     * 解析通过「B」接口提供方获取的物流信息
     * json 解析: jackson, https://www.baeldung.com/jackson
     *
     * @param jsonStr 物流信息,json 格式字符串
     * @return 封装后的物流信息
     */
    public static LogisticsInfo getLogisticsInfoByB(String jsonStr) throws IOException {
        LogisticsInfo logisticsInfo = new LogisticsInfo();
        ObjectMapper objectMapper = new ObjectMapper();
        JsonNode rootNode = objectMapper.readTree(jsonStr);
        String comCode = rootNode.get("comCodeB") != null ? rootNode.get("comCodeB").asText() : "";
        String postNo = rootNode.get("postNoB") != null ? rootNode.get("comCodeB").asText() : "";
        boolean success = rootNode.get("successB") != null ? rootNode.get("successB").asBoolean() : false;
        String failReason = rootNode.get("failReasonB") != null ? rootNode.get("failReasonB").asText() : "";
        int state = rootNode.get("stateB") != null ? rootNode.get("stateB").asInt() : 0;

        logisticsInfo.setComCode(comCode);
        logisticsInfo.setPostNo(postNo);
        logisticsInfo.setSuccess(success);
        logisticsInfo.setFailReason(failReason);
        logisticsInfo.setState(state);
        /**
         * 遍历物流轨迹
         */
        if (rootNode.get("tracesB") != null && rootNode.get("tracesB").size() > 0) {
            List<LogisticsTrace> logisticsTraceList = new ArrayList<>(16);
            LogisticsTrace logisticsTrace;
            JsonNode traceNode = rootNode.get("tracesB");
            for (int i = 0; i < traceNode.size(); i++) {
                logisticsTrace = new LogisticsTrace();
                logisticsTrace.setAcceptTime(traceNode.get(i).get("acceptTimeB").asText());
                logisticsTrace.setAcceptStation(traceNode.get(i).get("acceptStationB").asText());
                logisticsTraceList.add(logisticsTrace);
            }
            logisticsInfo.setLogisticsTraceList(logisticsTraceList);
        }
        return logisticsInfo;
    }



    private LogisticsUtils(){}
}

作者写了两个处理方法是因为不同的快递公司API查询接口返回的字段是不一样的,需要整合成统一的物流信息。这里的 xxA 和 xxB 只是为了举例,具体依然要根据实际业务来定。
这里的关键知识点为 JsonNode, 其作为数据载体,既可以装一个对象,也可以装一个数组,因此可以用它来解析嵌套的 json 数据。

3.3 测试

测试类: LogisticsUtilsTest

package com.ljq.demo.util;

import com.ljq.demo.bean.LogisticsInfo;
import org.junit.Test;

import java.io.IOException;

public class LogisticsUtilsTest {

    @Test
    public void getLogisticsInfoByA() throws IOException {

        String jsonStrA = "{" +
                "            \"comCodeA\": \"YTO\"," +
                "            \"postNoA\": \"M0101065279\"," +
                "            \"successA\": true," +
                "            \"failReasonA\": \"\"," +
                "            \"stateA\": 3," +
                "            \"tracesA\": [" +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-10 20:01:04\"," +
                "                    \"acceptStationA\": \"【上海市德玛西亚公司】 已收件\"" +
                "                }," +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-10 22:20:33\"," +
                "                    \"acceptStationA\": \"【上海市德玛西亚公司】 已打包\"" +
                "                }," +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-10 22:23:13\"," +
                "                    \"acceptStationA\": \"【上海市德玛西亚公司】 已发出 下一站 【上海转运中心】\"" +
                "                }," +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-11 03:07:34\"," +
                "                    \"acceptStationA\": \"【上海转运中心】 已收入\"" +
                "                }," +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-11 03:14:21\"," +
                "                    \"acceptStationA\": \"【上海转运中心】 已发出 下一站 【杭州转运中心】\"" +
                "                }," +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-11 10:37:02\"," +
                "                    \"acceptStationA\": \"【杭州转运中心】 已收入\"" +
                "                }," +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-11 13:11:00\"," +
                "                    \"acceptStationA\": \"【杭州转运中心】 已发出 下一站 【石桥转运中心】\"" +
                "                }," +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-11 22:45:20\"," +
                "                    \"acceptStationA\": \"【石桥转运中心】 已收入\"" +
                "                }," +
                "                {" +
                "                    \"acceptTimeA\": \"2018-09-12 13:19:39\"," +
                "                    \"acceptStationA\": \"客户 签收人: 圆通代签 已签收 感谢使用圆通速递,期待再次为您服务\"" +
                "                }" +
                "            ]" +
                "        }" +
                "}";
        LogisticsInfo logisticsInfo = LogisticsUtils.getLogisticsInfoByA(jsonStrA);
        System.out.println("logisticsInfo: " + logisticsInfo);
    }

}

断点查看:

jackson-json

OK,使用 jackson 解析复杂 json 的内容就是这些了。

源码查看: https://github.com/Flying9001/Demo

关于 json 与 java 对象的转换,可以参考 jackson 官方文档: https://www.baeldung.com/jackson-object-mapper-tutorial

这里列出几项感受一下:


ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
objectMapper.writeValue(new File("target/car.json"), car);

String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Car car = objectMapper.readValue(json, Car.class);  
String jsonCarArray = 
  "[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});

使用 jackson 处理 json 就这么简单。

最后,推荐一下作者的个人公众号,主要分享个人技术与思考,有兴趣的可以关注:

404Code

猜你喜欢

转载自blog.csdn.net/Mrqiang9001/article/details/82872405
今日推荐