1. 程式人生 > >java使用jackson解析複雜json字串

java使用jackson解析複雜json字串

為什麼要用 jackson

jackson 憑藉其簡潔的語法、高效的處理速度、豐富的功能、清晰的文件等眾多優勢,受到廣大開發者的熱愛,成為了java程式設計師處理json資料的不二選擇。

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 的內容就是這些了。

這裡列出幾項感受一下:


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