一.介绍与使用
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) > 0 ) true 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; } }
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); } } }
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)); } }