• 使用JSLT进行对象转换


    一.介绍与使用

    JSLT是一种完整的JSON查询和转换语言。git地址:https://github.com/schibsted/jslt

    java中使用:

    <dependency>
    <groupId>com.schibsted.spt.data</groupId>
    <artifactId>jslt</artifactId>
    <version>0.1.11</version>
    <scope>compile</scope>
    </dependency>

    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.schibsted.spt.data.jslt.Expression;
    import com.schibsted.spt.data.jslt.Parser;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import java.io.IOException;
    
    @Slf4j
    public class JsltUtil {
        /**
         * 转换json字符串(忽略异常,当异常时返回null)
         * @param jsltStr   jslt表达式
         * @param inputStr  json字符串入参
         * @return
         */
        public static String jsonAdapt(String jsltStr, String inputStr) {
            try {
                return jsonAdapt(jsltStr, inputStr, true);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("jsonAdapt error jsltStr={},inputStr={}", jsltStr, inputStr, e);
            }
            return null;
        }
    
        /**
         * 转换json字符串
         * @param jsltStr   jslt表达式
         * @param inputStr  json字符串入参
         * @param ignoreException  是否忽略异常 如果为ture,出现异常后返回null
         * @return
         */
        public static String jsonAdapt(String jsltStr, String inputStr, boolean ignoreException) throws IOException {
            if (StringUtils.isBlank(jsltStr) || StringUtils.isBlank(inputStr)) {
                log.info("jsonAdapt param illegal");
                return null;
            }
            try {
                ObjectMapper mapper = new ObjectMapper();
                JsonNode input = mapper.readTree(inputStr);
                Expression jslt = Parser.compileString(jsltStr);
                JsonNode output = jslt.apply(input);
                return output.toString();
            } catch (Exception e) {
                log.error("jsonAdapt error jsltStr={},inputStr={}", jsltStr, inputStr, e);
                if (!ignoreException) {
                    throw e;
                }
            }
            return null;
        }
    }
    

      

     

    二.常用转换场景示例

     

    1.普通对象转换

    // jslt表达式
    // crowd 根据age进行if判断输出人群
    {
        "name":.name,
        "age":.age,
        "crowd":if(.age<18"未成年" else if(.age<60"成年人" else "老年人"
    }
    // 入参:
    {
        "name":"nameTest",
        "age":10
    }
    // 结果:
    {
      "name" "nameTest",
      "age" 10,
      "crowd" "未成年"
    }

     

    2.子对象转换

    // jslt表达式
    // * : . 匹配输入对象中的所有键,除了那些已经指定的,并将它们复制到输出对象中;如果有部分字段不想全拷贝使用 :* - bar, baz, quux : .
    // 注意一点, * :  这个语法必须在最后,否则会报错    子对象中,没效果 "extendMsg3":{*:.}   这个字段直接不输出(只有当对象名和入参的一样时,才能在嵌套中再用*  ,eg:"extendMsg":{*:.} 是生效的)
    {
    "extendMsg1":.extendMsg,
    "extendMsg2":{"married2":.extendMsg.married},
    *:.
    }
    // 入参:
    {
        "name":"nameTest",
        "age":10,
        "extendMsg":{"sex":"男","married":false,"education":"小学生"}
    }
    // 结果:
    {
      "extendMsg1" : {
        "sex" "男",
        "married" false,
        "education" "小学生"
      },
      "extendMsg2" : {
        "married2" false
      },
      "name" "nameTest",
      "age" 10,
      "extendMsg" : {
        "sex" "男",
        "married" false,
        "education" "小学生"
      }
    }

     

    3.子对象平铺展开/对象缩进 转换

    // 展开
    // jslt表达式
    {
    "sex":.extendMsg.sex,
    "married":.extendMsg.married,
    * - extendMsg:.
    }
    // 入参:
    {
        "name":"nameTest",
        "age":10,
        "extendMsg":{"sex":"男","married":false,"education":"小学生"}
    }
    // 结果:
    {
      "sex" "男",
      "married" false,
      "name" "nameTest",
      "age" 10
    }
     
     
     
    // 缩进
    // jslt表达式
    {
    "name":.name,
    "age":.age,
    "extendMsg":{"sex":.sex,"married":.married,"education":.education}
    }
    // 入参:
    {
        "name":"nameTest",
        "age":10,
        "sex":"男",
        "married":false,
        "education":"小学生"
    }
    // 结果:
    {
      "name" "nameTest",
      "age" 10,
      "extendMsg" : {
        "sex" "男",
        "married" false,
        "education" "小学生"
      }
    }

     

    4.集合转换

    /** 把集合全部转换 */
    // jslt表达式
    [for(.) {*:.}]
    // 入参:
    [
        {
            "name":"nameTest",
            "age":10,
            "sex":"男",
            "married":false,
            "education":"小学生"
        },
        {
            "name":"nameTest2",
            "age":102,
            "sex":"女",
            "married":true,
            "education":"女博士"
        }
    ]
    // 结果:
    // 输出和入参相同
     
     
    /** 集合部分转换 */
    // jslt表达式
    [for(.) {"name":.name,"age":.age,"test":"testxxx"}]
    // 结果:
    [ {
      "name" "nameTest",
      "age" 10,
      "test" "testxxx"
    }, {
      "name" "nameTest2",
      "age" 102,
      "test" "testxxx"
    } ]

     

    5.复杂对象(分页为例)转换

    /** 1.复杂组合=基础+集合 转换;2.haveNextPaga 举例变量计算 */
    // jslt表达式
    {
    "pageIndex":.pageNo,
    "pageSize":.pageSize,
    "total":.total,
    "haveNextPaga":if((.total - .pageNo * .pageSize) > 0true else false,
    "data":[for(.list) {*:.}]
    }
    // 入参:
    {
        "pageNo":1,
        "pageSize":2,
        "total":100,
        "list":[
            {
                "name":"nameTest",
                "age":10,
                "sex":"男",
                "married":false,
                "education":"小学生"
            },
            {
                "name":"nameTest2",
                "age":102,
                "sex":"女",
                "married":true,
                "education":"女博士"
            }
        ]
    }
    // 结果:
    {
      "pageIndex" : 1,
      "pageSize" : 2,
      "total" : 100,
      "haveNextPaga" : true,
      "data" : [ {
        "name" : "nameTest",
        "age" : 10,
        "sex" : "男",
        "married" : false,
        "education" : "小学生"
      }, {
        "name" : "nameTest2",
        "age" : 102,
        "sex" : "女",
        "married" : true,
        "education" : "女博士"
      } ]
    }

     

    6.取集合中的某一个对象

    // jslt表达式
    {"data1":.[0].name,"data2":.[0].age}
    // 入参:
    [
        {
            "name":"nameTest",
            "age":10,
            "sex":"男",
            "married":false,
            "education":"小学生"
        },
        {
            "name":"nameTest2",
            "age":102,
            "sex":"女",
            "married":true,
            "education":"女博士"
        }
    ]
    // 结果:{
     
      "data1" "nameTest",
      "data2" 10
    }

     

    三.可视化界面生成表达式

    可进行两个事情:1.实现入下所示通过列表自动生成jslt表达式;2.通过json生成列表


    生成的jslt表达式: 

    {"a1":.a, "b1":{"c1":.c, "d1":.d}, "e1":.e, "g1":[for(.g){"a2":.ga, "b2":.gb}]}

    入参json:

    {"a":"a","c":"c","d":"d","e":"e","g":[{"ga":"ga","gb":"gb"},{"ga":"ga2","gb":"gb2"},{"ga":"ga3","gb":"gb3"}]}

    出参json:

    {"a1":"a","b1":{"c1":"c","d1":"d"},"e1":"e","g1":[{"a2":"ga","b2":"gb"},{"a2":"ga2","b2":"gb2"},{"a2":"ga3","b2":"gb3"}]}

    ObjectAdaptMappingInfoBO (配置对象)
    public class ObjectAdaptMappingInfoBO implements Serializable {
        /**
         * id
         */
        private Long id;
    
        /**
         * 对象映射id
         */
        private Long adaptId;
    
        /**
         * 原始字段
         */
        private String sourceColumn;
    
        /**
         * 目标字段
         */
        private String targetColumn;
    
        /**
         * 对象类型:1:基本类型,2:object,3:集合
         * @see ObjectAdaptTypeEnum
         */
        private Byte targetType;
    
        /**
         * 创建时间
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
        private Date createTime;
    
        /**
         * 创建人
         */
        private String creator;
    
        /**
         * 更新时间
         */
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
        private Date updateTime;
    
        /**
         * 修改人
         */
        private String updator;
    
        /**
         * 是否有效 0无效 1有效
         */
        private Byte yn;
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public Long getAdaptId() {
            return adaptId;
        }
    
        public void setAdaptId(Long adaptId) {
            this.adaptId = adaptId;
        }
    
        public String getSourceColumn() {
            return sourceColumn;
        }
    
        public void setSourceColumn(String sourceColumn) {
            this.sourceColumn = sourceColumn;
        }
    
        public String getTargetColumn() {
            return targetColumn;
        }
    
        public void setTargetColumn(String targetColumn) {
            this.targetColumn = targetColumn;
        }
    
        public Byte getTargetType() {
            return targetType;
        }
    
        public void setTargetType(Byte targetType) {
            this.targetType = targetType;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = createTime;
        }
    
        public String getCreator() {
            return creator;
        }
    
        public void setCreator(String creator) {
            this.creator = creator;
        }
    
        public Date getUpdateTime() {
            return updateTime;
        }
    
        public void setUpdateTime(Date updateTime) {
            this.updateTime = updateTime;
        }
    
        public String getUpdator() {
            return updator;
        }
    
        public void setUpdator(String updator) {
            this.updator = updator;
        }
    
        public Byte getYn() {
            return yn;
        }
    
        public void setYn(Byte yn) {
            this.yn = yn;
        }
    }
    View Code
    JsltConvertUtil (转换工具类)
    @Slf4j
    public class JsltConvertUtil {
        /**
         * 通过mapping配置,生成jslt表达式
         * @param list
         * @param type
         * @param parentName
         * @return
         */
        public static String convert(List<ObjectAdaptMappingInfoBO> list, Byte type, String parentName) {
            String result = "";
    
            String collectionName = "";
            List<ObjectAdaptMappingInfoBO> selfList = getSelfList(list, type, parentName);
            if (CollectionUtils.isEmpty(selfList)) {
                return returnConvertByType(result, type, collectionName);
            }
            Set<String> existColumn = new HashSet<>();
            for (ObjectAdaptMappingInfoBO mappingInfoBO : selfList) {
                try {
                    if (mappingInfoBO == null || StringUtil.isBlank(mappingInfoBO.getTargetColumn())) {
                        continue;
                    }
                    String targetColumn = mappingInfoBO.getTargetColumn();
                    String sourceColumn = mappingInfoBO.getSourceColumn();
                    // 如果原字段没有对应值,不返回
                    if (StringUtil.isBlank(sourceColumn)) {
                        continue;
                    }
                    if (ObjectAdaptTypeEnum.COLLECTION.getCode().equals(type)) {
                        String[] split = sourceColumn.split("\.");
                        collectionName = split[0];
                        sourceColumn = split.length > 1 ? split[1] : split[0];
                    }
    
                    boolean haveChild = false;
                    if (targetColumn.contains(".")) {
                        haveChild = true;
                        String[] split = targetColumn.split("\.");
                        targetColumn = split[0];
                    }
                    // targetColumn之前已添加过,跳出
                    if (!existColumn.add(targetColumn)) {
                        continue;
                    }
    
                    // 不是第一次,字符串就不为空,添加一个分割符
                    if (result != "") {
                        result += ", ";
                    }
                    result += """ + targetColumn + "":";
                    if (!haveChild) {
                        result += "." + sourceColumn;
                    } else {
                        // 还有子层级,递归调用
                        result += convert(selfList, mappingInfoBO.getTargetType(), targetColumn);
                    }
                } catch (Exception e) {
                    log.error("JsltConvertUtil convert error list={}, type, parentName", JSON.toJSONString(list), type, parentName, e);
                }
            }
    
    
            return returnConvertByType(result, type, collectionName);
        }
    
        /**
         * 返回结果处理
         * @param result
         * @param type
         * @return
         */
        private static String returnConvertByType(String result, Byte type, String collectionName) {
            if (ObjectAdaptTypeEnum.OBJECT.getCode().equals(type)) {
                result = "{" + result + "}";
            } else if (ObjectAdaptTypeEnum.COLLECTION.getCode().equals(type)) {
                result = "[for(." + collectionName + "){" + result + "}]";
            }
    
            return result;
        }
    
        /**
         * 获取本次要处理的集合
         * @param list
         * @param type
         * @param parentName
         * @return
         */
        private static List<ObjectAdaptMappingInfoBO> getSelfList(List<ObjectAdaptMappingInfoBO> list, Byte type, String parentName) {
            if (CollectionUtils.isEmpty(list)) {
                return null;
            }
            // 如果parentName isBlank 直接返回所有
            if (StringUtil.isBlank(parentName)) {
                return list;
            }
    
            List<ObjectAdaptMappingInfoBO> selfList = new ArrayList<>();
            for (ObjectAdaptMappingInfoBO mappingInfoBO : list) {
                if (mappingInfoBO == null || mappingInfoBO.getTargetType() == null) {
                    continue;
                }
    
                String targetColumn = mappingInfoBO.getTargetColumn();
                if (mappingInfoBO.getTargetType().equals(type) && targetColumn.startsWith(parentName + ".")) {
                    ObjectAdaptMappingInfoBO mappingInfoBONew = new ObjectAdaptMappingInfoBO();
                    BeanUtils.copyProperties(mappingInfoBO, mappingInfoBONew);
                    // 把父去掉,生成新对象
                    mappingInfoBONew.setTargetColumn(targetColumn.replaceFirst(parentName + ".", ""));
                    selfList.add(mappingInfoBONew);
                }
            }
            return selfList;
        }
    
        /**
         * 将json字符串转为目标转换对象集合
         * @param str
         * @return
         */
        public static List<ObjectAdaptMappingInfoBO> jsonStrToTargetList(String str) {
            List<ObjectAdaptMappingInfoBO> list = new ArrayList<>();
            Map map = JSON.parseObject(str, Map.class);
            for (Object key : map.keySet()) {
                jsonStrConvertMappingBOList(list, (String) key, map.get(key), null);
            }
            return list;
        }
    
        /**
         * 将json字符串转为来源转换对象集合
         * @param str
         * @return
         */
        public static List<ObjectAdaptMappingInfoBO> jsonStrToSourceList(String str) {
            List<ObjectAdaptMappingInfoBO> mappingInfoBOList = jsonStrToTargetList(str);
            List<ObjectAdaptMappingInfoBO> result = new ArrayList<>();
            for (ObjectAdaptMappingInfoBO mappingInfoBO : mappingInfoBOList) {
                ObjectAdaptMappingInfoBO sourceMappingInfoBO = new ObjectAdaptMappingInfoBO();
                sourceMappingInfoBO.setSourceColumn(mappingInfoBO.getTargetColumn());
                result.add(sourceMappingInfoBO);
            }
            return result;
        }
    
        /**
         * 对象转换
         * @param list
         * @param parentName
         * @param value
         * @param type
         */
        private static void jsonStrConvertMappingBOList(List<ObjectAdaptMappingInfoBO> list, String parentName, Object value, Byte type) {
            if (value != null && JSON.class.isAssignableFrom(value.getClass())) {
                // 判断对象任然是JSON
                if (JSONObject.class.isAssignableFrom(value.getClass())) {
                    JSONObject oo = (JSONObject) value;
                    for (Map.Entry<String, Object> stringObjectEntry : oo.entrySet()) {
                        String key = stringObjectEntry.getKey();
                        Object value2 = stringObjectEntry.getValue();
                        String nextName = parentName == null ? key : parentName + "." + key;
                        if (JSONObject.class.isAssignableFrom(value2.getClass())) {
                            jsonStrConvertMappingBOList(list, nextName, value2, ObjectAdaptTypeEnum.OBJECT.getCode());
                        } else if (JSONArray.class.isAssignableFrom(value2.getClass())) {
                            jsonStrConvertMappingBOList(list, nextName, value2, ObjectAdaptTypeEnum.COLLECTION.getCode());
                        } else {
                            ObjectAdaptMappingInfoBO mappingInfoBO = new ObjectAdaptMappingInfoBO();
                            if (type == null) {
                                mappingInfoBO.setTargetType(ObjectAdaptTypeEnum.OBJECT.getCode());
                            } else {
                                mappingInfoBO.setTargetType(type);
                            }
                            mappingInfoBO.setTargetColumn(nextName);
                            addToList(list, mappingInfoBO);
                        }
                    }
                } else if (JSONArray.class.isAssignableFrom(value.getClass())) {
                    JSONArray oo = (JSONArray) value;
                    for (Object o : oo) {
                        jsonStrConvertMappingBOList(list, parentName, o, ObjectAdaptTypeEnum.COLLECTION.getCode());
                    }
                }
            } else {
                ObjectAdaptMappingInfoBO mappingInfoBO = new ObjectAdaptMappingInfoBO();
                if (type == null) {
                    mappingInfoBO.setTargetType(ObjectAdaptTypeEnum.BASIC.getCode());
                } else {
                    mappingInfoBO.setTargetType(type);
                }
                mappingInfoBO.setTargetColumn(parentName);
                addToList(list, mappingInfoBO);
    
            }
        }
    
        /**
         * 添加到集合,需要判断是否要可以添加
         * @param list
         * @param mappingInfoBO
         */
        private static void addToList(List<ObjectAdaptMappingInfoBO> list, ObjectAdaptMappingInfoBO mappingInfoBO) {
            Set<String> collect = list.stream().map(ObjectAdaptMappingInfoBO::getTargetColumn).collect(Collectors.toSet());
            if (collect.add(mappingInfoBO.getTargetColumn())) {
                list.add(mappingInfoBO);
            }
        }
    }
    View Code
    JsltConvertUtilTest (测试方法) 
    public class JsltConvertUtilTest {
        /**
         * | sourceColumn | targetColumn | type       |
         * | ------------ | ------------ | ---------- |
         * | a            | a1           | basic      |
         * | c            | b1.c1        | object     |
         * | d            | b1.d1        | object     |
         * | e            | e1           | basic      |
         * | g.ga         | g1.a2        | collection |
         * | g.gb         | g1.b2        | collection |
         *
         * 生成的jslt表达式:
         * {"a1":.a, "b1":{"c1":.c, "d1":.d}, "e1":.e, "g1":[for(.g){"a2":.ga, "b2":.gb}]}
         *
         * 原json:
         * {"a":"a","c":"c","d":"d","e":"e","g":[{"ga":"ga","gb":"gb"},{"ga":"ga2","gb":"gb2"},{"ga":"ga3","gb":"gb3"}]}
         *
         * jslt转换后json:
         * {"a1":"a","b1":{"c1":"c","d1":"d"},"e1":"e","g1":[{"a2":"ga","b2":"gb"},{"a2":"ga2","b2":"gb2"},{"a2":"ga3","b2":"gb3"}]}
         * @param args
         */
        public static void main(String[] args) {
    
            List<ObjectAdaptMappingInfoBO> list = new ArrayList<>();
    
            ObjectAdaptMappingInfoBO mappingInfoBO = new ObjectAdaptMappingInfoBO();
            mappingInfoBO.setSourceColumn("a");
            mappingInfoBO.setTargetColumn("a1");
            mappingInfoBO.setTargetType(ObjectAdaptTypeEnum.BASIC.getCode());
            list.add(mappingInfoBO);
    
            ObjectAdaptMappingInfoBO mappingInfoBO2 = new ObjectAdaptMappingInfoBO();
            mappingInfoBO2.setSourceColumn("c");
            mappingInfoBO2.setTargetColumn("b1.c1");
            mappingInfoBO2.setTargetType(ObjectAdaptTypeEnum.OBJECT.getCode());
            list.add(mappingInfoBO2);
    
            ObjectAdaptMappingInfoBO mappingInfoBO3 = new ObjectAdaptMappingInfoBO();
            mappingInfoBO3.setSourceColumn("d");
            mappingInfoBO3.setTargetColumn("b1.d1");
            mappingInfoBO3.setTargetType(ObjectAdaptTypeEnum.OBJECT.getCode());
            list.add(mappingInfoBO3);
    
            ObjectAdaptMappingInfoBO mappingInfoBO4 = new ObjectAdaptMappingInfoBO();
            mappingInfoBO4.setSourceColumn("e");
            mappingInfoBO4.setTargetColumn("e1");
            mappingInfoBO4.setTargetType(ObjectAdaptTypeEnum.OBJECT.getCode());
            list.add(mappingInfoBO4);
    
            ObjectAdaptMappingInfoBO mappingInfoBO5 = new ObjectAdaptMappingInfoBO();
            mappingInfoBO5.setSourceColumn("");
            mappingInfoBO5.setTargetColumn("f1");
            mappingInfoBO5.setTargetType(ObjectAdaptTypeEnum.OBJECT.getCode());
            list.add(mappingInfoBO5);
    
            ObjectAdaptMappingInfoBO mappingInfoBO6 = new ObjectAdaptMappingInfoBO();
            mappingInfoBO6.setSourceColumn("g.ga");
            mappingInfoBO6.setTargetColumn("g1.a2");
            mappingInfoBO6.setTargetType(ObjectAdaptTypeEnum.COLLECTION.getCode());
            list.add(mappingInfoBO6);
    
            ObjectAdaptMappingInfoBO mappingInfoBO7 = new ObjectAdaptMappingInfoBO();
            mappingInfoBO7.setSourceColumn("g.gb");
            mappingInfoBO7.setTargetColumn("g1.b2");
            mappingInfoBO7.setTargetType(ObjectAdaptTypeEnum.COLLECTION.getCode());
            list.add(mappingInfoBO7);
    
            System.out.println(JsltConvertUtil.convert(list, ObjectAdaptTypeEnum.OBJECT.getCode(), null));
    
            testStrToMapping();
        }
    
        /**
         * 测试str 转为 配置信息
         */
        private static void testStrToMapping() {
            String str = "{"a1":"a","b1":{"c1":"c","d1":"d","a3":{"t":"tt"},"a4":[1,2,3]},"e1":"e","g1":[{"b2":"gb"},{"a2":"ga2","b2":"gb2"},{"a2":"ga3","b2":"gb3"}]}";
            List<ObjectAdaptMappingInfoBO> mappingInfoBOList = JsltConvertUtil.jsonStrToTargetList(str);
            System.out.println(JSON.toJSONString(mappingInfoBOList));
        }
    }
    View Code
  • 相关阅读:
    zoj 3279 线段树 OR 树状数组
    fzu 1962 树状数组 OR 线段树
    hdu 5057 块状链表
    hdu3487 Play with Chain
    bzoj 1588营业额统计(HNOI 2002)
    poj2823 Sliding Window
    poj2828 Buy Tickets
    poj2395 Out of Hay
    poj3667 Hotel
    poj1703 Lost Cows
  • 原文地址:https://www.cnblogs.com/dengding/p/14202651.html
Copyright © 2020-2023  润新知