Camel组件-JSONata组件

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" ]

通配符:

"*":

使用*来代替字段名

  1. Address.* ==>
[ "Hursley Park", "Winchester", "SO21 2JN" ]

返回Address下的所有属性对应值.

  1. *.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组件转换后, 添加""或添加标签.

你可能感兴趣的:(Camel组件-JSONata组件)