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