java使用jackson解析复杂json字符串
为什么要用 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);
}
}
断点查看:
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 就这么简单。
最后,推荐一下作者的个人公众号,主要分享个人技术与思考,有兴趣的可以关注: