Camel数据转换-XML<->JSON

基本概念

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() 文本节点
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,122评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,070评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,491评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,636评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,676评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,541评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,292评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,211评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,655评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,846评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,965评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,684评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,295评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,894评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,012评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,126评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,914评论 2 355

推荐阅读更多精彩内容