【实用技巧】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,性能还有待考量哈。

posted @ 2024-04-23 21:53  酷酷-  阅读(2947)  评论(0编辑  收藏  举报