java使用jackson解析複雜json字串
阿新 • • 發佈:2019-02-14
為什麼要用 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 資料存在巢狀的現象(快遞的物流軌跡屬於巢狀的,而且是陣列
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);
}
}
斷點檢視:
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 就這麼簡單。
最後,推薦一下作者的個人公眾號,主要分享個人技術與思考,有興趣的可以關注: