Camel官网资料: https://camel.apache.org/components/latest/jsonata-component.html
JSONata官网资料: http://docs.jsonata.org/overview.html
Jsonata组件允许你使用JSONATA规范语法进行JSON与JSON之间的消息转换,或者JSON与其他消息之间的转换。
Maven用户使用该组件需添加相关依赖:
org.apache.camel.springboot
camel-jsonata-starter
x.x.x
URL格式:
jsonata:specName[?options]
- spaceName: 调用转换规则的文件路径
组件属性:
名称 | 描述 | 默认状态 | 类型 |
---|---|---|---|
lazyStartProducer | 生产者懒加载机制. 延迟启动可避免生产者启动过程中发生异常导致路由启动失败. | false | Boolean |
autowiredEnabled | 是否启动自动装配. 通过在注册表中进行查找是否存在匹配类型的实例, 将该实例作为自动装配实例并在组件上进行配置. 例: JDBC数据源. | true | Boolean |
JSLT VS JSONATA:
- 在JSLT转换规则中,支持if,else,else if等分支语句;JSONATA不支持上述分支语句,其支持三目运算法"?",具体示例下面会展示.
- 使用JSNATA组件时在进行JSON转换后, 应重新将参数进行设置(
- JSONATA组件在发起时应在路径中规定入参格式, 即&inputType=JsonString; JSLT不需要.
JSONata语法:
数据示例:
{
"FirstName": "Fred",
"Surname": "Smith",
"Age": 28,
"Address": {
"Street": "Hursley Park",
"City": "Winchester",
"Postcode": "SO21 2JN"
},
"Phone": [
{
"type": "home",
"number": "0203 544 1234"
},
{
"type": "office",
"number": "01962 001234"
},
{
"type": "office",
"number": "01962 001235"
},
{
"type": "mobile",
"number": "077 7700 1234"
}
],
"Email": [
{
"type": "work",
"address": ["[email protected]", "[email protected]"]
},
{
"type": "home",
"address": ["[email protected]", "[email protected]"]
}
],
"Other": {
"Over 18 ?": true,
"Misc": null,
"Alternative.Address": {
"Street": "Brick Lane",
"City": "London",
"Postcode": "E1 6RF"
}
}
}
基础语法:
对于基本数据格式:
Surname ==> "Smith"
Age ==> 28
Address.City ==> "Winchester"
Other.Misc ==> "null"
Other.'Over 18 ?' ==> true
对于数组:
返回第一个数据:
Phone[0] ==>
{ "type": "home", "number": "0203 544 1234" }
返回最后一个数据:
Phone[-1] ==>
{ "type": "mobile", "number": "077 7700 1234" }
倒序返回数据:
Phone[-2] ==
{ "type": "office", "number": "01962 001235" }
指定字段:
Phone[0].number ==>
"0203 544 1234"
未指定索引:
Phone.number ==>
[ "0203 544 1234", "01962 001234", "01962 001235", "077 7700 1234" ]
通过索引数组返回数据:
Phone[[0..2]] ==>
[
{ "type": "home", "number": "0203 544 1234" },
{ "type": "office", "number": "01962 001234" },
{ "type": "office", "number": "01962 001235" },
]
返回指定属性数据:
Phone[type = 'mobile'] ==>
{ "type": "mobile", "number": "077 7700 1234" }
Phone[type = 'mobile'].number ==>
"077 7700 1234"
Phone[type = 'office'].number ==>
[ "01962 001234", "01962 001235" ]
特殊的, 利用"[]"来进行返回数组, 如上述例子中:
Phone[][type='mobile'].number ==>
[ "077 7700 1234" ]
通配符:
"*":
使用*来代替字段名
- Address.* ==>
[ "Hursley Park", "Winchester", "SO21 2JN" ]
返回Address下的所有属性对应值.
- *.Postcode ==>
"SO21 2JN"
返回子对象中Postcode对应值.
仅返回单节点下对应字段属性值.
"**":
无视子节点深度, 加强版"*.字段名"
**.Postcode ==>
[ "SO21 2JN", "E1 6RF" ]
不仅返回Address下的Postcode值, 同时返回Other.Alternative.Address.Postcode对应值.
常用函数:
在Jsonata中有很多函数, 它与Java中的用法类似. 大致如下:
字符函数:
$string(arg, prettify)
arg有以下规则:
- 字符串不可被改变.
- 函数可以转换空字符串.
- 无穷大的数字与NaN将会抛出异常, 因为其不能表示为Json.
- JSON.stringify函数将所有值转换为Json格式.
注意事项:
- 如果arg未指定(即不带任何参数调用此函数), 则将上下文的值用作arg.
- 如果prettify为true, 则转换为"prettified"JSON.(每行自动缩进).
示例:
$string(12345) => "12345"
[1,2,3].$string => ["1", "2", "3"]
$length(str)
返回字符串中的字符数.
- 如果str未指定(即不带任何参数调用此函数), 则将上下文值用作str.
- 如果str不是字符串, 则会抛出异常.
示例:
$length("Hello World") => 11
$substring(str, start[, length])
返回指定长度和指定开始下标的目标字符串.
- 如果str未指定(即仅使用数字参数调用此函数), 则将上下文值用作str.
- 如果str不是字符串, 则会抛出异常.
- 如果length指定, 则返回字符串将包含指定length长度.
- 如果start为负, 表示从尾部开始的字符数.
示例:
$substring("Hello World", 3) => "lo World"
$substring("Hello World", 3, 5) => "lo Wo"
$substring("Hello World", -4) => "orld"
$substring("Hello World", -4, 2) => "or"
$substringBefore(str, chars)
返回的字符串为"chars"在"str"中第一次出现前的子字符串.
- 如果str未指定(即仅使用一个参数调用此函数), 则将上下文值用作str.
- 如果str不包含chars, 则返回str.
- 如果str和chars不是字符串, 则会抛出异常.
示例:
$substringBefore("Hello World", " ") => "Hello"
$substringAfter(str, chars)
返回的字符串为"chars"在"str"中第一次出现后的子字符串.
- 如果str未指定(即仅使用一个参数调用此函数), 则将上下文值用作str.
- 如果str不包含chars, 则返回str.
- 如果str和chars不是字符串, 则会抛出异常.
示例:
$substringAfter("Hello World", " ") => "World"
$uppercase(str)
返回一个字符串, 其中str的所有字符都转换为大写.
- 如果str未指定(即不带任何参数调用此函数), 则将上下文值用作str.
- 如果str不是字符串, 则会抛出异常.
示例:
$uppercase("Hello World") => "HELLO WORLD"
$lowercase(str)
返回一个字符串, 其中str的所有字符都转换为小写.
- 如果str未指定(即不带任何参数调用此函数), 则将上下文值用作str.
- 如果str不是字符串, 则会抛出异常.
示例:
$lowercase("Hello World") => "hello world"
$trim(str)
str字符串将通过以下步骤来规范和修改所有空格字符:
所有制表符, 回车符和换行符均替换为空格.
连续的空格符修改为单个空格.
首尾空格已删除.
如果str未指定(即不带任何参数调用此函数), 则将上下文值用作str.
如果str不是字符串, 则会抛出异常.
示例:
$trim(" Hello \n World ") => "Hello World"
$pad(str, width [, char])
返回字符串str的副本并对它额外填充. 并且它的字符的总数是width参数的绝对值.
- 如果width是正数, 则该字符串被填充到右侧; 如果为负数, 则将其填充到左侧.可选的char参数指定填充字符(多个)使用.
- 如果未指定, 则默认为空格字符.
示例:
$pad("foo", 5) => "foo "
$pad("foo", -5) => " foo"
$pad("foo", -5, "#") => "##foo"
$formatBase(35, 2) ~> $pad(-8, '0') => "00100011"
$contains(str, pattern)
str符合pattern的匹配规则, 返回true, 否则返回false. 该pattern参数可以是字符串或正则表达式(regex).
- 如果str未指定(即使用一个参数调用此函数), 则将上下文值用作str.
- 如果是字符串, pattern的字符在str中存在且连续则返回true, 否则该函数将返回str.
- 如果它是正则表达式, 则当该正则表达式与的内容匹配时返回true, 否则该函数将返回str.
示例:
$contains("abracadabra", "bra") => true
$contains("abracadabra", /a.*a/) => true
$contains("abracadabra", /ar.*a/) => false
$contains("Hello World", /wo/) => false
$contains("Hello World", /wo/i) => true
Phone[$contains(number, /^077/)] => { "type": "mobile", "number": "077 7700 1234" }
$split(str, separator [, limit])
将str参数拆分为子字符串数组. 该separator参数可以是字符串或正则表达式(regex).
- 如果str未指定, 则将上下文值用作str.
- 如果str不是字符串, 则会抛出异常.
- 如果是字符串, 则指定str应该在其中拆分的字符.
- 如果为空字符串, str将被拆分为单个字符的数组.
- 如果它是正则表达式, 将字符串与匹配该正则表达式的任何字符分开.
- limit参数是一个数字, 它指定包含在结果数组中的最大子字符串数, 任何其他子字符串都将被丢弃.
- 如果limit未指定, 则将str完全拆分, 对结果的数组大小没有限制.
- 如果limit不是非负数, 则会抛出异常.
示例:
$split("so many words", " ") => [ "so", "many", "words" ]
$split("so many words", " ", 2) => [ "so", "many" ]
$split("too much, punctuation. hard; to read", /[ ,.;]+/) => ["too", "much", "punctuation", "hard", "to", "read"]
$join(array[, separator])
将单个字符串拼接成一个字符串中, 每个组件字符串可由separator参数分隔.
- 如果输入数组包含不是字符串的项目, 则会抛出异常.
- 如果separator未指定, 则默认为空字符串, 即各个字符串之间没有分隔符.
- 如果separator不是字符串, 则会抛出异常.
示例:
$join(['a','b','c']) => "abc"
$split("too much, punctuation. hard; to read", /[ ,.;]+/, 3) ~> $join(', ') => "too, much, punctuation"
$match(str, pattern [, limit])
将str字符串与pattern正则表达式进行匹配, 返回一个对象数组, 每个对象都包含每次匹配的字符串.
每个对象包含以下字段:
match 通过正则表达式匹配的子字符串.
index 匹配所需要偏移的字符数.
groups 如果正则表达式包含括号, 则它表示所匹配的字符串的数组.
如果str未指定, 则将上下文值用作str.
如果str不是字符串, 则会抛出异常.
示例:
$match("ababbabbcc",/a(b+)/) =>
[
{
"match": "ab",
"index": 0,
"groups": ["b"]
},
{
"match": "abb",
"index": 2,
"groups": ["bb"]
},
{
"match": "abb",
"index": 5,
"groups": ["bb" ]
}
]
$base64encode()
Base64加密.
示例:
$base64encode("myuser:mypass") ===> "bXl1c2VyOm15cGFzcw=="
$base64decode()
Base64解密.
示例:
$base64decode("bXl1c2VyOm15cGFzcw==") ===> "myuser:mypass"
$encodeUrlComponent(str)
对路径参数和资源符进行加密.
示例:
$encodeUrlComponent("?x=test") ===> "%3Fx%3Dtest"
$encodeUrl(str)
对路径参数进行加密.
示例:
$encodeUrl("[https://mozilla.org/?x=](https://mozilla.org/?x=)шеллы") ===> "[https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B](https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B)"
$decodeUrlComponent(str)
解密
示例:
$decodeUrlComponent("%3Fx%3Dtest") ===> "?x=test"
$decodeUrl(str)
解密
示例:
$decodeUrl("[https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B"](https://mozilla.org/?x=%D1%88%D0%B5%D0%BB%D0%BB%D1%8B")) ===> "[https://mozilla.org/?x=](https://mozilla.org/?x=)шеллы"
布尔型函数:
$exists(arg)
判断arg是否存在, 存在则返回true, 不存在返回false, 可在该方法基础上进行参数判断.
- 如果arg不穿则会抛出异常.
示例:
对于
{
"req": "logout",
"agentId": **.workNumber,
"tenantid": "default"
}
$exists(retCode) => false
对于
{
"resp" : {
"respCode" : retCode = "1" ? "0" : retCode
}
}
$exists(retCode) => true
时间函数:
$now([picture [, timezone]])
返回符合ISO 8601格式的UTC时间戳的字符串.
- 如果定义了时间格式和时区(即picture与timezone), 则会返回对应的格式, 与$fromMillis()方法类似.
示例:
$now() => "2021-03-26T10:16:34.152Z"
$millis()
将返回的时间进行毫秒处理.
示例:
$millis() => 1616724968087
$fromMillis(number [ , picture [ , timezone]])
将传入的number按照对应的规则与时区进行转换.
- 如果picture未进行传值, 默认采用ISO 8601格式转换.
- 如果提供了picture参数, 那么将按照该参数定义的规则进行格式化. 特殊的, 此函数的定义规则与Xpath/XQuery函数中的fn: format-dateTime的参数版本应保持一致.
- 如果提供了timezone参数, 那么格式化的时间应在该定义时区. timezone的格式为"±HHMM", 加减为时区偏移量. UTC以东的为正偏移, UTC以西的为负偏移.
示例:
$fromMillis(1510067557121) => "2017-11-07T15:12:37.121Z"
$fromMillis(1510067557121, '[M01]/[D01]/[Y0001] [h#1]:[m01][P]') => "11/07/2017 3:12pm"
$fromMillis(1510067557121, '[H01]:[m01]:[s01] [z]', '-0500') => "10:12:37 GMT-05:00"
其他运算符:
&
合并运算符, 将字符串拼接.
如果该运算中参数不为字符串, 将先执行$string方法进行转换.
示例:
"Hello" & "World" => "HelloWorld"
? :
三目运算符, 可根据实际情况返回相对应结果.
示例:
Price < 50 ? "Cheap" : "Expensive"
- Price为属性, 其对应值如果小于50则返回"Cheap", 反之"Expensive".
- 其中判断条件和响应结果可自定义.
- 可与其他函数结合使用.(如下述实例).
:=
自定义函数, 函数规则可以是函数式也可以是常量.
示例:
$four := 4
$square := function($n) { $n * $n }
~>
函数连接链, 可将多层嵌套函数进行简化书写.
示例:
$uppercase($substringBefore($substringAfter(Customer.Email, "@"), ".")) =====>
Customer.Email ~> $substringAfter("@") ~> $substringBefore(".") ~> $uppercase()
$sum(Account.Order.Product.(Price * Quantity)) =====>
Account.Order.Product.(Price * Quantity) ~> $sum()
该运算符也可以与上述:=结合使用.
如:
$uppertrim := $trim ~> $uppercase;
$uppertrim(" Hello World ") ==>
"HELLO WORLD"
- 该示例为创建一个新函数"trim"与"$uppercase"两个方法结合.
- 可根据实际情况设置相关函数.
... ~> | ... | ... |
对象转换操作符, 可进行参数值的改变或其部分属性的删除.
语法结构:
head ~> | location | update [, delete] |
- head为要进行相关操作的属性变量.
- location为操作的属性变量中需要改变的部分.
- update为需要更新的location中的属性, 并在更新完后重新整合在location中.
- delete为在执行相关操作后, 对指定属性进行删除, 可省略.
示例:
| Account.Order.Product | { 'Price' : Price * 1.2 } |
该例为将Product属性中的Price值增加20%. 当需要多次调用时, 可与上述":="或"~>"相结合:
$increasePrice := |Account.Order.Product|{'Price': Price * 1.2}| 或 payload ~> |Account.Order.Product|{'Price': Price * 1.2}|
同时可以对变量添加属性:
| Account.Order.Product | { 'Price' : Price * 1.2 , 'Total' : Price * Quantity} |
该表达式为变量添加一个新属性, 但需要注意的是, "Total"中的"Price"指的是原属性中"Price"对应的值, 而不是改变后的Price值.
可以删除变量中相关属性:
$ ~> |Account.Order.Product|{'Total': Price * Quantity}, ['Price', 'Quantity']|
对于"Product", 在每次运行时都会添加一条"Total"属性, 同时会删除其"Price"与"Quantity"属性.
实例:
电话渠道的坐席签出操作:
传入参数:
xml参数:
Accept : application/xml
b23abb6d451346efa13370172d1921ef
00000001
Json传参:
Accept : application/json
{
"signOff" : {
"appId" : "b23abb6d451346efa13370172d1921ef",
"workNumber" : "00000001"
} }
路由代码:
${header.Accept} == 'application/xml'
- xj: Camel组件, 详情看xj组件文档.
fl_signOff.json:
{
"req": "logout",
"agentId" : **.workNumber,
"tenantid":"default"
}
- 实现参数名称转换.
- ** : Jsonata特定语法, 直接找到目标节点, 忽略其是否有父节点. 该特性解决XML文件转换时忽略掉根节点的问题.
fl_signOff_response.json:
{
"resp" : {
"respCode" : retCode = "1" ? "0" : retCode
}
}
- 实现参数名称转换.
- 三目运算符解决参数判断(可嵌套判断, 可自定义响应结果).
整合参数:
$exists(retCode) = true
?
({
"resp" : {
"respCode" : retCode = "1" ? "0" : retCode
}
})
:
{
"req": "logout",
"agentId": **.workNumber,
"tenantid": "default"
}
- $exists()判断该次响应是否发送给freeLink.其中括号里的字段名为freeLink处理该请求后响应的字段.
- 运用"?"该运算符目的为将两次参数格式转换文件整合在一个文件中, 方便维护. 同样可以将两次参数格式转换文件分别写出.
日志打印:
[图片上传失败...(image-94022d-1667401091691)]
[图片上传失败...(image-aab07e-1667401091691)]
相关异常以及解决方法(踩坑)
- com.api.jsonata4java.expressions.ParseException: 此异常为编写转换规则中语法不规范或标点符号导致, 检查转换规则中的语句编写书否有错误或标点符号是否正确.
- No body available of type: java.io.InputStream but has value: 此异常为在接收参数并转换后发送请求时, 未重新设置参数导致. 解决方法为在通过JSONATA组件转换后, 添加"
"或添加 标签.