【实用技巧】JSON格式转换方式
1 前言
对接开发中,常遇到的就是报文转换。比如从淘宝或者京东拉取订单,亦或是各个公司内部的WMS、OMS等交互,都涉及到格式转换。而大多的格式基本上都是 JSON 格式,当然也有一些老的 SAP 交互用的是 XML格式的,还有一小部分 webService 接口也是用的 XML 格式。
那我们这节就看看JSON报文转换,当然最简单最直接的方式就是硬编码,直接写 set get 啥的,或者一些 JSON 网站上有那种 JSON 转 JavaBean 的,比如我们常用的 json.cn等,我这里主要是列两种不是那么硬的。
2 实践
2.1 测试准备
我这里有两种实体,比如左边是我们的第三方平台订单信息,右边我们自己系统的订单信息,那么当要拉取各种第三方平台的订单的时候,不可避免的就是我们中间的转换,也是我们本节要讲的。
第三方订单信息:
// 第三方订单主信息 @Data public class ThirdOrderEntity { /** * 订单号 */ private String flowNo; /** * 订单类型 */ private Integer tradeType; /** * 下单时间 */ private LocalDateTime payTime; /** * 订单金额 */ private BigDecimal orderAmount; /** * 订单备注 */ private String remark; /** * 明细信息 */ private List<ThirdOrderDetailEntity> skuList; } // 第三方订单明细信息 @Data public class ThirdOrderDetailEntity { /** * 商品编码 */ private String sku; /** * 数量 */ private BigDecimal num; }
订单信息:
// 订单主信息 @Data public class OrderEntity { /** * 订单号 */ private String orderNo; /** * 订单类型 */ private Integer orderType; /** * 下单时间 */ private LocalDateTime orderTime; /** * 订单金额 */ private BigDecimal totalAmount; /** * 订单备注 */ private String orderRemark; /** * 明细信息 */ private List<OrderDetailEntity> detailList; } // 订单明细信息 @Data public class OrderDetailEntity { /** * 商品编码 */ private String skuCode; /** * 数量 */ private BigDecimal quantity; }
2.2 JsonPath 正则转换
我这就直接贴代码了哈:
package com.example.demo.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.example.demo.entity.OrderEntity; import com.example.demo.entity.third.ThirdOrderDetailEntity; import com.example.demo.entity.third.ThirdOrderEntity; import com.google.common.collect.Lists; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; /** * @author: kuku * @description */ public class JsonConvert { /** * 从规则生成转换正则,主要针对目标jsonPath和源jsonPath * @param map * @return */ private static Map<String, String> convertRules(Map<String, String> map) { Map<String,String> ruleMap = new HashMap<String, String>(); for (Map.Entry<String,String> entry:map.entrySet()){ //针对目标路径进行转换 String key = "/"+ entry.getKey() .replaceAll("\\[(\\d+)\\]","/\\$$1") .replace(".","/"); //针对源路径进行转换 String value = "/"+ entry.getValue() .replaceAll("\\[\\]","/(\\\\d+)") .replace(".","/\\d*/*"); ruleMap.put(key,value); } return ruleMap; } /** * json映射转换,返回转换后的json数据 * @param jobj 源json数据 * @param map map的key为目标jsonPath,value为源jsonPath,需要换为实际的映射对象,并传入类型和值集转换相关信息 * @return */ private static JSONObject exchangeJson(JSONObject jobj,Map<String,String> map) { Map<String, String> ruleMap = convertRules(map); JSONObject newJson = new JSONObject(); Map<String,Object> paths = JSONPath.paths(jobj); for (Map.Entry<String,Object> pathEntry : paths.entrySet()){ for (Map.Entry<String,String> ruleEntry:ruleMap.entrySet()){ if(pathEntry.getKey().matches(ruleEntry.getValue())){ String newPath = pathEntry.getKey().replaceAll(ruleEntry.getValue(),ruleEntry.getKey()); //获取转换后的value。此处进行类型转换和值集映射转换 Object newValue = pathEntry.getValue(); if(JSONPath.contains(newJson,newPath) && JSONPath.size(newJson,newPath) < 0){ //path下已经存在一个值,则需要将类型转换为jsonArray并添加第二个值 Object tmp = JSONPath.eval(newJson,newPath); Object[] tArray = {tmp,newValue}; JSONPath.set(newJson, newPath, tArray); }else if(JSONPath.size(newJson,newPath) > 1){ //path下已经存在数组对象,直接追加 JSONPath.arrayAdd(newJson, newPath, newValue); }else{ //path不存在,直接添加对象 JSONPath.set(newJson, newPath, newValue); } } } } return newJson; } public static void main(String[] args) { // 平台订单信息 ThirdOrderEntity thirdOrderEntity = new ThirdOrderEntity(); thirdOrderEntity.setFlowNo("1111"); thirdOrderEntity.setOrderAmount(new BigDecimal("111.2")); thirdOrderEntity.setPayTime(LocalDateTime.now()); thirdOrderEntity.setRemark("我是订单备注"); thirdOrderEntity.setTradeType(2); ThirdOrderDetailEntity one = new ThirdOrderDetailEntity(); one.setSku("PE-OEF-WER-002"); one.setNum(new BigDecimal("2")); ThirdOrderDetailEntity two = new ThirdOrderDetailEntity(); two.setSku("WER-OEF-WER-002"); two.setNum(new BigDecimal("99")); thirdOrderEntity.setSkuList(Lists.newArrayList(one, two)); // 第三方平台的 JSNO String sourceJson = JSON.toJSONString(thirdOrderEntity); // JSON转换规则 InputStream resourceAsStream = JsonConvert.class.getResourceAsStream("/testfile/changeOrder.json"); String changeRule = new BufferedReader(new InputStreamReader(resourceAsStream)).lines().collect(Collectors.joining(System.lineSeparator())); JSONArray array = JSON.parseArray(changeRule); // 规则放进 map Map<String,String> map = new HashMap<>(16); for (Object o : array) { JSONObject jsonObject = (JSONObject) o; String oldPath = jsonObject.getString("oldPath"); String newPath = jsonObject.getString("newPath"); if (StringUtils.isNotBlank(oldPath) && StringUtils.isNotBlank(newPath)) { map.put(newPath, oldPath); } } JSONObject jobj = JSON.parseObject(sourceJson); String res = exchangeJson(jobj, map).toJSONString(); System.out.println(res); OrderEntity orderEntity = JSON.parseObject(res, OrderEntity.class); System.out.println(orderEntity); } }
转换规则:
[
{
"newPath":"orderNo",
"oldPath":"flowNo"
},
{
"newPath":"totalAmount",
"oldPath":"orderAmount"
},
{
"newPath":"orderTime",
"oldPath":"payTime"
},
{
"newPath":"orderRemark",
"oldPath":"remark"
},
{
"newPath":"orderType",
"oldPath":"tradeType"
},
{
"newPath":"detailList[1].skuCode",
"oldPath":"skuList[].sku"
},
{
"newPath":"detailList[1].quantity",
"oldPath":"skuList[].num"
}
]
效果:
2.3 json-convertor
另外一中方式是我在 gitee上找的一个:json-convertor,我们来试试:
package com.example.demo.service; import io.shmilyhe.convert.JsonConvertor; import io.shmilyhe.convert.tools.ResourceReader; /** * @author: kuku * @description */ public class JsonTest { public static void main(String[] args) { String json = ResourceReader.read("testfile/test1.json"); String commands =ResourceReader.read("testfile/test1.script"); //一个脚本需只初始化一次,初始化脚本会做一次内存的编译,比较耗时。脚本初始化后执行的效率很高。 JsonConvertor jc = new JsonConvertor(commands); String dest = jc.convert(json); System.out.println(dest); } }
效果是一样的:
3 小结
感谢两位铁子的文章,JsonPath转换以及json-convertor,性能还有待考量哈。