基本概念
Apache Camel中的数据格式转换,可以采用默认的marshalling、unmarshalling处理模式。
marshalling: POJO -> XML
unmarshalling: XML -> POJO
对于复杂的数据转换可以使用XJ组件,详细标准见官网资料
XJ组件可以将文档进行XML或JSON两种格式转换, 无需java对象。同时支持自定义转换规则, 方便快捷。XJ组件扩展了XSLT组件, 因此支持XSLT组件提供的所有选项。
XSLT由W3C制定,可以将XML文档转换为另一种格式的文档,如HTML、纯文本或另一个XML文档。通过使用XSLT,可以对XML文档进行生成、过滤、排序、聚合、格式化等功能。XSLT不仅可以转换XML文档的内容,还可以为XML文档生成样式。
XSLT模板采用模式匹配的方式,选择对应的XML元素进行转换。一个模板通常包括一个匹配模式和一些转换指令。
在学习XJ组件之前,需先了解XSLT组件规范,配套的可以查阅 中文资料。
XSLT文档核心由以下三个部分组成:
xsl:stylesheet:定义XSLT文档的根元素。
xsl:template:规定XML文档中某个元素如何被转换。
xsl:value-of:输出XML文档中某个元素的值。
<xsl:template match="xxxx">
<xsl:variable name="name" select="@xj:name"/>
<xsl:element name="{$name}">
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
Maven用户使用该组件需添加相关依赖:
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-xj</artifactId>
<version>x.x.x</version>
<!-- use the same version as your Camel core version -->
</dependency>
URL格式
xj:templateName?transformDirection=XML2JSON | JSON2XML[&options]
templateName : 转换格式模板名称 (Camel默认或自定义).
transformDirection: 交换方向. 该选项为必选项且二选一 (XML2JSON | JSON2XML).
options : 选项 (见表格).
例: 自定义转换格式:
xj:classpath/JSON2XML.xsl | XML2JSON.xsl?transformDirection=JSON2XML | XML2JSON
classpath : 自定义转换格式文件路径.
组件选项
Camel提供了十个组件, 如下所示:
名称 | 描述 | 默认状态 | 类型 |
---|---|---|---|
contentCache | 加载资源文件时生成缓存. 如果设置false那么Camel将在每次进行消息处理都会重新加载, 优化性能.在JMX在运行时可以使用clearCachedStylesheet操作强制重新加载已缓存的资源文件. | true | Boolean |
lazyStartProducer | 生产者懒加载机制. 延迟启动可避免生产者启动过程中发生异常导致路由启动失败. | false | Boolean |
autowiredEnabled | 是否启动自动装配. 通过在注册表中进行查找是否存在匹配类型的实例, 将该实例作为自动装配实例并在组件上进行配置. 例: JDBC数据源. | true | Boolean |
saxonConfiguration | 使用自定义Saxon配置. | Configuration | |
saxonConfigurationProperties | 设置自定义Saxon配置属性. | Map | |
saxonExtensionFunctions | 允许使用自定义的net.sf.saxon.lib.ExtensionFunctionDefinition. 需要将camel-saxon添加到类路径. 该函数在注册表中查找, 可以在其中逗号分隔多个值以进行查找. | String | |
transformerFactoryConfigurationStrategy | 使用类名为FQN的自定义的XSLT变压器工厂. | String | |
transformerFactoryConfigurationStrategy | TransformerFactory实例的配置策略. | TransformerFactoryConfigurationStrategy | |
uriResolver | 要使用自定义UriResolver. 不应与选项"uriResolverFactory"一起使用. | URIResolver | |
uriResolverFactory | 使用依赖于动态端点资源URI的自定义UriResolver. 不应与选项"uriResolver"一起使用. | XsltUriResolverFactory |
XJ端点使用URL语法配置:
xj:resourceUri
相关参数:
路径参数:
名称 | 描述 | 默认状态 | 类型 |
---|---|---|---|
resourceUri | 模板的必填路径. 默认URIResolver支持以下内容: |
(1) 可以添加以下前缀:classpath, file, http, ref或bean.
(2) classpath, file和http使用这些协议加载资源(默认为classpath); ref将在注册表中查找资源; bean将在bean上调用一个方法用作资源
(3) 对于bean,您可以在点后指定方法名称, 例如bean:myBean.myMethod | | String |
查询参数:
名称 | 描述 | 默认状态 | 类型 |
---|---|---|---|
allowStAX | 是否允许将StAX用作javax.xml.transform.Source. 如果XSLT库支持StAX, 例如Saxon库, 则可以启用此功能. Xalan库(JVM中的默认库)不支持StAXSource. | true | Boolean |
contentCache | 加载资源文件时生成缓存. 如果设置false那么Camel将在每次进行消息处理都会重新加载, 优化性能.在JMX在运行时可以使用clearCachedStylesheet操作强制重新加载已缓存的资源文件. | true | Boolean |
deleteOutputFile | 如果输出的是文件, 此属性表示在完成ExChange处理后是否删除输出文件. | false | Boolean |
failOnNullBody | 如果输入的内容为null, 是否报出异常. | true | Boolean |
lazyStartProducer | 生产者懒加载机制. 延迟启动可避免生产者启动过程中发生异常导致路由启动失败. | false | Boolean |
output | 指定使用哪种输出类型的选项. 可能的值为string, bytes, DOM, file. | ||
前三个选项基于内存, 其中文件直接流式传输到java.io.File. 对于文件, 您必须在IN标头中使用密钥Exchange.XSLT_FILE_NAME指定文件名, 该文件名也是CamelXsltFileName. 需要注意的是, 必须写出指向文件名的路径, 否则会在运行时引发异常. 可以支持枚举, 值可以是以下之一: 字符串, 字节, DOM, 文件. | string | XsltOutPut | |
transformDirection | 转换方向.必须是JSON2XML或者XML2JSON. | TransformDirection | |
transformerCacheSize | javax.xml.transform.Tranformer的缓存可重复使用, 避免调用Template.newTransformer(). | 0 | Int |
entityResolver | 要将自定义org.xml.sax.EntityResolver与javax.xml.transform.sax.SAXSource一起使用. | EntityResolver | |
errorListener | 允许配置自定义的监听器(javax.xml.transform.ErrorListener). 需要注意的是, 当该监听器监听到相关致命错误时, 会将信息存储在ExChange上, 然后使用默认监听器. | ErrorListener | |
resultHandlerFactory | 允许使用自定义的org.apache.camel.builder.xml.ResultHandlerFactory, 它能够使用自定义的org.apache.camel.builder.xml.ResultHandler类型. | ResultHandlerFactory | |
saxonConfiguration | 自定义Saxon配置 | Configuration | |
saxonExtensionFuntions | 允许使用自定义的net.sf.saxon.lib.ExtensionFunctionDefinition. 您将需要将camel-saxon添加到类路径. 该函数在注册表中查找, 可以在其中用逗号分隔多个值以进行查找. | String | |
TransformFactory | 使用自定义XSLT变压器工厂. | TransformFactory | |
TransformerFactoryClass | 指定FQN类名称的自定义XSLT变压器工厂 | String | |
TransformFactoryConfiguration | 创建新的TransformFactory实例的配置策略. | TransformerFactoryConfigurationStrategy | |
uriResolver | 支持使用自定义的javax.xml.transform.URLResolver | URL解析器 |
使用XJ端点:
JSON转换XML:
未自定义XSLT模板:
示例(Camel内置转换规则):
代码访问:
from("direct:start").
to("xj:identity?transformDirection=JSON2XML");
XML访问:
<route xmlns="http://camel.apache.org/schema/spring" id="start">
<from uri="rest:get:start"/>
<convertBodyTo type="String"/>
<to uri="xj:identity?transformDirection=JSON2XML"/>
</route>
传参:
{
"firstname": "camel",
"lastname": "apache",
"personalnumber": 42,
"active": true,
"ranking": 3.1415926,
"roles": [
"a",
{
"x": null
}
],
"state": {
"needsWater": true
}
}
响应结果:
<?xml version="1.0" encoding="UTF-8"?>
<object xmlns:xj="http://camel.apache.org/component/xj" xj:type="object">
<object xj:name="firstname" xj:type="string">camel</object>
<object xj:name="lastname" xj:type="string">apache</object>
<object xj:name="personalnumber" xj:type="int">42</object>
<object xj:name="active" xj:type="boolean">true</object>
<object xj:name="ranking" xj:type="float">3.1415926</object>
<object xj:name="roles" xj:type="array">
<object xj:type="string">a</object>
<object xj:type="object">
<object xj:name="x" xj:type="null">null</object>
</object>
</object>
<object xj:name="state" xj:type="object">
<object xj:name="needsWater" xj:type="boolean">true</object>
</object>
</object>
Tips:
- XJ元数据节点都在"http://camel.apache.org/component/xj"命名空间里.
- 键值对key值均存储在xj:name中.
- xj:type对应的全部类型均在示例中展出.
- 生成的XML元素为Object对象.
自定义XSLT模板:
示例:
转换模板:(其中相关匹配策略为XPath表达式, 详情文档底部)
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xj="http://camel.apache.org/component/xj"
exclude-result-prefixes="xj">
<xsl:output omit-xml-declaration="no" encoding="UTF-8" method="xml" indent="yes"/>
<xsl:template match="/">
<person>
<xsl:apply-templates select="//object"/>
</person>
</xsl:template>
<xsl:template match="object[@xj:type != 'object' and @xj:type != 'array' and string-length(@xj:name) > 0]">
<xsl:variable name="name" select="@xj:name"/>
<xsl:element name="{$name}">
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="@*|node()"/>
</xsl:stylesheet>
代码访问:
from("direct:start").
to("xj:com/example/json2xml.xsl?transformDirection=JSON2XML");
XML访问:
<route xmlns="http://camel.apache.org/schema/spring" id="start">
<from uri="rest:get:start"/>
<convertBodyTo type="String"/>
<to uri="xj:classpath/json2xml.xsl?transformDirection=JSON2XML"/>
</route>
响应结果:
<?xml version="1.0" encoding="UTF-8"?>
<person>
<firstname>camel</firstname>
<lastname>apache</lastname>
<personalnumber>42</personalnumber>
<active>true</active>
<ranking>3.1415926</ranking>
<x>null</x>
<needsWater>true</needsWater>
</person>
XML转换JSON:
未自定义模板:
示例:
代码访问:
from("direct:second").
to("xj:identity?transformDirection=XML2JSON");
XML访问:
<route xmlns="http://camel.apache.org/schema/spring" id="second">
<from uri="rest:get:second"/>
<convertBodyTo type="String"/>
<to uri="xj:identity?transformDirection=XML2JSON"/>
</route>
传参:
<?xml version="1.0" encoding="UTF-8"?>
<person>
<firstname>camel</firstname>
<lastname>apache</lastname>
<personalnumber>42</personalnumber>
<active>true</active>
<ranking>3.1415926</ranking>
<roles>
<entry>a</entry>
<entry>
<x>null</x>
</entry>
</roles>
<state>
<needsWater>true</needsWater>
</state>
</person>
响应结果:
{
"firstname": "camel",
"lastname": "apache",
"personalnumber": "42",
"active": "true",
"ranking": "3.1415926",
"roles": [
"a",
{
"x": "null"
}
],
"state": {
"needsWater": "true"
}
}
Tips:
- XML根元素可以以某种方式命名, 并始终在"{}"中.
- JSON键值为XML中元素的名称.
- 当同名的元素存在, 会自动生成JSON数组.
自定义XSLT模板:
示例:
转换模板:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xj="http://camel.apache.org/component/xj"
exclude-result-prefixes="xj">
<xsl:output omit-xml-declaration="no" encoding="UTF-8" method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="personalnumber">
<xsl:element name="{local-name()}">
<xsl:attribute name="xj:type">
<xsl:value-of select="'int'"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="active|needsWater">
<xsl:element name="{local-name()}">
<xsl:attribute name="xj:type">
<xsl:value-of select="'boolean'"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="ranking">
<xsl:element name="{local-name()}">
<xsl:attribute name="xj:type">
<xsl:value-of select="'float'"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="roles">
<xsl:element name="{local-name()}">
<xsl:attribute name="xj:type">
<xsl:value-of select="'array'"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*[normalize-space(text()) = 'null']">
<xsl:element name="{local-name()}">
<xsl:attribute name="xj:type">
<xsl:value-of select="'null'"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
代码访问:
from("direct:start").
to("xj:com/example/xml2json.xsl?transformDirection=XML2JSON");
XML访问:
<route xmlns="http://camel.apache.org/schema/spring" id="start">
<from uri="rest:get:start"/>
<convertBodyTo type="String"/>
<to uri="xj:classpath/xml2json.xsl?transformDirection=JSON2XML"/>
</route>
响应结果:
{
"firstname": "camel",
"lastname": "apache",
"personalnumber": 42,
"active": true,
"ranking": 3.1415926,
"roles": [
"a",
{
"x": null
}
],
"state": {
"needsWater": true
}
}
可以看出无论通过自定义转换或是官方转换, 其返回数据大致相同. 需要注意的是:
- xj:type可让指定所需的输出类型.
- xj:name可指定json中key值名称.
存在问题
- XML转换JSON格式时丢失根节点.
实例:
电话渠道座席的签出操作:
请求参数:
xml传参需设置Accept : application/xml
[图片上传失败...(image-23bbfc-1709218573962)]
<?xml version="1.0" encoding="UTF-8"?>
<signOff>
<appId>b23abb6d451346efa13370172d1921ef</appId>
<workNumber>00000001</workNumber>
</signOff>
JSON传参需设置Accept : application/json
[图片上传失败...(image-b7bc60-1709218573963)]
{
"signOff" : {
"appId" : "b23abb6d451346efa13370172d1921ef",
"workNumber" : "00000001"
} }
发送请求具体代码:
<!--电话渠道座席的签出操作-->
<route xmlns="http://camel.apache.org/schema/spring" id="signOff">
<from uri="rest:post:signOff"/>
<convertBodyTo type="String"/>
<when>
<!--XML传参-->
<simple>${header.Accept} == 'application/xml'</simple>
<to uri="xj:camelXML/X2J.xsl?transformDirection=XML2JSON"/>
</when>
<!--传入参数转换-->
<to uri="jslt:camelJSON/fl_logout.json?contentCache=true"/>
<to uri="http:{{freelink.server.url}}?bridgeEndpoint=true"/>
<!--响应参数转换-->
<to uri="jslt:camelJSON/fl_logout.json?contentCache=true"/>
<log message="response from server ${body}"/>
</route>
相关标签:
- convertBodyTo: 相当于SetBody, 设置请求参数.
- when: 判断分支. 此处判断前端发来的参数格式为XML或者JSON格式.
- jstl: Camel插件, 实现JSON与JSON的转换, 详细看jstl组件文档. 前者传参转换, 后者响应转换.
XML转换结果打印:
[图片上传失败...(image-56af4f-1709218573963)]
通过查看打印结果可以看出, XML格式的参数已经转换为JSON格式参数, 但也可以发现原参数根节点在转换的时候丢失. 后利用Jsonata组件解决该问题, 详情看Jsonata组件介绍.
附录表
Camel可用类型
object | 生成json对象 |
---|---|
array | 生成json数组 |
string | 生成json字符串 |
int | 生成不带小数部分的json数值 |
float | 生成带小数部分的json数值 |
boolean | 生成一个json布尔值 |
null | 生成空值null |
常用的XPath表达式
记号 | 含义 |
---|---|
a | 上下文节点的a元素 |
* | 上下文节点的所有元素 |
a/b | 以上下文节点的a元素为父节点的b元素 |
a//b | 以上下文节点的a元素为祖先的b元素 |
a|b | 上下文节点的a元素和b元素 |
a[表达式] | 符合表达式的上下文节点的a元素 |
. | 上下文节点 |
.. | 上下文节点的父节点 |
/ | 根节点 |
@a | 上下文节点的a属性 |
@* | 上下文节点的所有属性 |
node() | 所有节点 |
text() | 文本节点 |