jolt插件使用简介

jolt插件的主要作用是将一种json格式转化为另外一种json格式,它通过使用配置文件定义转换规则的方法,使得json转换可配置,只要输入的json格式是固定的(这一点很重要)就可以使用jolt进行转换。本篇主要介绍常用的几种转换方式,感兴趣的童鞋可以前往GitHub学习该插件的时服用方法。jolt插件GitHub地址:https://github.com/bazaarvoice/jolt/tree/jolt-0.1.0#Overview

一、jolt插件主要转换方法与基本使用
插件提供了5种转换方法:
(1)shift:主要作用是拷贝,将输入json的某个结构拷贝到目标json
(2)default:主要作用是设置默认值,如果输入json某个value不存在,则使用默认值补齐
(3)remove:主要作用是删除,可以删除输入json结构的某个结构
(4)sort:对key值进行排序
(5)cardinality:jsonobject和jsonarray之间的切换

注:5种转换方法分别对应了5个Java类:ShiftrDefaultrRemovrSortrCardinalityTransform ,如果定义的转换规则中只是使用到了以上转换中的一种,则可以使用这5个类的transform方法转换,如果转换规则中使用了两种或者两种以上的转换,则需要使用另外一个类Chainr中的转换方法。

Shiftr为例,java代码如下:

Object input = JsonUtils.classpathToObject("/jolt/Shiftr/sample01/input.json");  //读取input.json,如果input已经是一个java对象,则可以忽略这步
Object spec = JsonUtils.classpathToObject("/jolt/Shiftr/sample01/spec.json");   //使用Jolt自带的JsonUtils读取配置的转换规则spec.json作为一个object

Shiftr shiftr=new Shiftr(spec);  //创建shiftr对象,传入转化规则
Object output = shiftr.transform(input);   //转换input,得到output
System.out.println(JsonUtils.toJsonString(output));

input.json文件内容:

{
  "foo": {
    "barKey": "barValue",
    "bazKey": "bazValue"
  }
}

spec.json内容(具体怎样编写,后面会详细介绍):

{
  "foo": {
    "*": {
      "$": "test.&(1,0).id"
    }
  }
}

其他类型进行转换的时候与此类似,使用Chainr转换有点特殊,java代码如下:

Object input = JsonUtils.classpathToObject("/jolt/Chainr/sample01/input.json");
 List<Object> spec = JsonUtils.classpathToList("/jolt/Chainr/sample01/spec.json");     //和其他类型不同,此处配置的转换规则是一个jsonarray,需要转换为List

Chainr chainr = Chainr.fromSpec(spec);
Object output = chainr.transform(input);
System.out.println(JsonUtils.toJsonString(output));

chainr转换的时候,转换规则配置的是一个jsonarray,通过operation来区分不同的转换方法,operation取值有shift,default,remove,cardinality,sort,每一种转换的规则作为spec的value,如:

[{
  "operation": "shift",
  "spec": {
    "rating": {
      "quality": {
        "value": "SecondaryRatings.quality.Value",
        "max": "SecondaryRatings.quality.RatingRage"
      }
    }
  }
}]

项目中引入jolt很简单,只需要在pom文件中增加以下依赖:

<dependency>
    <groupId>com.bazaarvoice.jolt</groupId>
    <artifactId>jolt-core</artifactId>
    <version>${latest.jolt.version}</version>
</dependency>
<dependency>
    <groupId>com.bazaarvoice.jolt</groupId>
    <artifactId>json-utils</artifactId>
    <version>${latest.jolt.version}</version>
</dependency>

目前最新版本是2018年5月2日更新的0.1.1。

二、shift转换
shift转换应该是我们平时使用最多的转换方式。与该种转换对应的类为Shiftr 。首先看一下最简单的转换:
如果有以下的input(输入json):

扫描二维码关注公众号,回复: 5478815 查看本文章
{
	"rating": {
		"quality": {
			"value": 3,
			"max": 5
		}
	}
}

我们想得到如下的output(目标json):

{
	"SecondaryRatings": {
		"quality": {
			"Value": 3,
			"RatingRange": 5
		}
	}
}

我们对比发现,原始json格式中,rating.quality.value的3需要放置在SecondaryRatings.quality.Value这样的结构中,rating.quality.max的5需要放置在SecondaryRatings.quality.RatingRange这个层级中,使用jolt,我们就可以编写如下的转换规则(spec):

{
	"rating": {
		"rating": {
			"value": "SecondaryRatings.quality.Value",
			"max": "SecondaryRatings.quality.RatingRange"
		}
	}
}

我们看到,转换规则的结构和input的结构是一致的,只不过转换规则中的value,其实是一个路径信息,即需要将该值拷贝到output结构的哪一个路径。如以上的转换规则,就是指需要将rating.rating.value的值拷贝到SecondaryRatings.quality.Value

shift转换的基本用法就这么简单,不过,shift转换的最强大的地方,在于它的通配符。
(1)“*”通配符(仅可以匹配input的key,不能匹配value)
使用 “*”通配符的时候,需要保证输入json具有相同的层级结构,它可以匹配某一个层级结构中的所有key,也可以匹配一个key的部分字符。
匹配某层级所有key:

{
	"rating": {
		"quality": {
			"value": 3,
			"max": 5
		},
		"sharpness": {
			"value": 7,
			"max": 10
		}
	}
}

以上输入中,rating.qualityrating.sharpness具有相同的层级结构,可以使用rating.*来代替rating下面的所有key(qualitysharpness)。
匹配key的部分字符:

{
	"tag-Pro": "Awesome",
	"tag-Con": "Bogus"
}

以上输入中,key值tag-Protag-Con具有相同的开头"tag-",所以我们可以使用tag-*来匹配具有相同开头的tag-Protag-Con

(2)“&”通配符
“&”通配符有两个参数&(0,1),第一个参数0表示从当前层级回溯多少级去寻找所需key,第二个参数1表示取第几个匹配到的参数(0表示取整体,1表示取第一个匹配结果,2表示取第二个匹配结果)。参数可以省略,&=&0=&(0)=&(0,0)
例如,有如下输入:

{
	"foo": {
		"test-bar-1": {
			"bar-1": "test-1"
		},
		"test-baz-2": {
			"baz-2": "test-2"
		}
	}
}

我们想经过转换得到如下结果:

{
	"test": {
		"bar": {
			"bar-1": "test-1"
		},
		"baz": {
			"baz-2": "test-2"
		}
	}
}

我们就可以编写如下的转换规则:

{
	"foo":{
		"test-*-*":{
			"*":"test.&(1,1).&"
		}
	}
}

解释一下,转换规则中test-*-*用来匹原json中foo的子级结构test-bar-1test-baz-2,当匹配到之后,我们再用*来分别匹配下一层的bar-1baz-2bar-1,baz-2对应的值要放到哪里呢?我们想分别放到test.bar.bar-1test.baz.baz-2下面,所以转换规则中的&(1,1)表示从当前层级回溯1级取值,即test-*-*的值,而且我们想让key值为bar或者baz,所以我们就取第1个*匹配到的值,所以写为&(1,1)。然后对于第三层的bar-1baz-2,我们想把自身的key作为输出json的key,所以我们就取当前层级的key值,即&,或者&0,&(0,0)

(3)“$”通配符
$通配符和&通配符具有相同的语法结构,都有两个参数,“&”通配符通常用于获取输入Json中某个key或者某部分key作为目标Json结构的key,$通配符通常是将输入Json的key值作为目标json的value使用,如有以下输入:

{
	"foo": {
		"barKey":"barValue",
		"bazKey":"bazValue"
	}
}

我们想得到以下输出:

{
	"test": {
		"barKey": {
			"id": "barKey"
		},
		"bazKey": {
			"id": "bazKey"
		}
	}
}

那么,我们可以编写如下的转化规则:

{
	"foo": {
		"*": {
			"$": "test.&(1,0).id"
		}
	}
}

我们使用"*"通配符来匹配barKeybazKey,然后使用"$"通配符来获取barKeybazKey,并将其作为value,写入到test.barKey.idtest.bazKey.id中。

(4)“@”通配符
“@”通配符也有两个参数@(0,1),"@""$"用法类似,不过"@"取得是指定位置的value作为目标Json的value值,而“$”取的是指定位置的key作为目标Json的value值,对于(3)中的输入,我们修改转换规则中的"$""@",即

{
	"foo": {
		"*": {
			"@": "test.&(1,0).id"
		}
	}
}

经过转换后,输出就会变为

{
	"test": {
		"barKey": {
			"id": "barValue"
		},
		"bazKey": {
			"id": "bazValue"
		}
	}
}

对比(3)中的转换结果,发现只是将转换规则中的“$”修改为了“@”,原先的barKey,bazKey就会变为barValue,bazValue,通过对比很明显可以看到两种通配符的区别
(5)“#”通配符
“#”通配符最有用的一点是,可以按照输入Json某个字段的取值,对输出Json设置不同的值,例如:

{
	"hidden": {
		"true": {
			"#disabled": "clients.clientId"
		},
		"false": {
			"#enable": "clients.clientId"
		}
	}
}

当hidden=true的时候,就会向clients.clientId写入disabled,当hidden=false的时候,就会向clients.clientId写入enable
(6)“|”通配符
“|”表示“或”的逻辑,很容易理解,它只会对列出的字符进行匹配,如规则:

{
  "sex":{
    "male|female":{
      "#normal":"status"
    }
  }
}

当sex取值male或者female的时候,才会给status设值normal。

(7)jsonarray的转换。
jsonarray的转换比较简单,举个例子说明:
input:

{
	"Photos": ["AAA.jpg","BBB.jpg"]
}

spec:

{
	"Photos": {
		"*": "photo-&-url"
	}
}

output

{
	"photo-0-url": "AAA.jpg",
	"photo-1-url": "BBB.jpg"
}

通过"*"可以匹配所有的元素,&获取当前元素的索引,数组中取值的时候,0,1,2等代表第1,2,3个元素,*可以获取所有元素
三、default转换
default转换比较简单,对应的java类是Defaultr,主要作用是预先设置一些默认值,如果input中不存在该值,则使用默认值进行补齐。
如有如下输入:

{
	"Rating": 3,
	"SecondaryRatings": {
		"quality": {
			"Range": 7,
			"Value": 3,
			"Id": "quality"
		},
		"sharpness": {
			"Value": 4,
			"Id": "sharpness"
		}
	}
}

想得到这样的输出:

{
	"Rating": 3,
	"RatingRange": 5,
	"SecondaryRatings": {
		"quality": {
			"Range": 7,
			"Value": 3,
			"Id": "quality",
			"MaxLabel": "Great",
			"MinLabel": "Terrible",
			"DisplayType": "NORMAL"
		},
		"sharpness": {
			"Range": 5,
			"Value": 4,
			"Id": "sharpness",
			"DisplayType": "NORMAL"
		}
	}
}

可以设置转换规则:


{
	"RatingRange": 5,
	"SecondaryRatings": {
		"quality": {
			"MaxLabel": "Great",
			"MinLabel": "Terrible",
			"DisplayType": "NORMAL"
		},
		"sharpness": {
			"Range": 5
			
		},
		"*": {
			"DisplayType": "NORMAL"
		}
	}
}

可以看到,input中没有RatingRange=5,我们在spec的相应位置设置了default值,在output中就展现了该值,其他值的设置也是类似的。
四、remove转换
remove也很简单,作用是删除input中的某些值。如果需要删除input中的个别字段,而其他字段保持不变,建议使用该转换。
如有以下input:

{
	"ratings": {
		"Set1": {
			"a": "a",
			"b": "b"
		},
		"Set2": {
			"c": "c",
			"b": "b"
		}
	}	
}

我们想删除ratings.Set1.b以及ratings.Set2.b,那么spec规则可以这样写:

{
	"ratings": {
		"*": {
			"b": ""
		}
	}
}

使用"*"匹配ratings下面的所有元素,然后删除key=b的元素,转化后结果:

{
	"ratings": {
		"Set1": {
			"a": "a"
		},
		"Set2": {
			"c": "c"
		}
	}
}

以上主要介绍了shift,default,remove的使用,这三种也是经常会用到的三种转换方法。以下列举出一些例子,方便大家对规则的编写有个更深的了解。
五、转换实例
(1)拷贝JSONObject
输入一个JSONObject,想原封不动将该JSONObject拷贝到输出,我们可以使用如下规则:

{
	"*":{
		"@":"&0"
	}
}

我们使用“*”通配符匹配了JSONObject中的所有key,然后使用“@”通配符获取所有value,放到“&0”,即当前value所在的key中,最终经过转换,其实只是做了一次JSONObject的拷贝而已。
(2)拷贝JSONArray
输入一个JSONArray,想原封不动的将JSONArray拷贝到输出,转化规则:

{
  "*": {
    "@": "[&1]"
  }
}

此处使用“*”通配符匹配了数组中所有的元素,其实此处的“*”是数组中元素的索引,即0,1,2…,然后对于数组中的每一个元素,使用“@”通配符匹配value值,将value值放到[&1]即数组的对应的索引处,完成拷贝。
(3)多条件判断
存在一种情况,当输入参数中某两个值为特定值的时候,需要将某些字段设置为特定值。如以下Json结构中,当action等于starting,并且type等于computer的时候,我们需要向原Json结构中增加一个字段status=running。

{
	"id":"123",
	"action":"starting",
	"type":"computer"
}

我们可以编写如下转换规则:

{
  "id":"id",
  "type":"type",
  "@(0,action)":"action",
  "action":{
    "starting":{
      "@(2,type)":{
        "computer":{
          "#running":"status"
        }
      }
    }
  }
}

首先将id,type,action等参数进行拷贝,然后对action的取值进行判断,如果action等于starting,这时候需要继续判断type的取值,我们使用了@(2,type)表示从当前层级回溯2级去取type的值,然后对type进行判断,当type等于computer的时候,我们使用"#"通配符添加一个字段status,取值为running.
经过转化后,我们得到的结果如下:

{
	"action": "starting",
	"id": "123",
	"type": "computer",
	"status": "running"
}

猜你喜欢

转载自blog.csdn.net/m0_37674755/article/details/85530142