# XML 支持-处理 XML 有效负载

# XML 支持-处理 XML 有效负载

Spring Integration 的 XML 支持扩展了 Spring Integration 与以下组件的核心:

你需要在项目中包含此依赖项:

Maven

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-xml</artifactId>
    <version>5.5.9</version>
</dependency>

Gradle

compile "org.springframework.integration:spring-integration-xml:5.5.9"

这些组件使得在 Spring 集成中处理 XML 消息变得更加简单。消息传递组件使用以一系列格式表示的 XML,包括java.lang.Stringorg.w3c.dom.Documentjavax.xml.transform.Source的实例。但是,如果需要 DOM 表示(例如,为了计算 XPath 表达式),则将String有效负载转换为所需的类型,然后将其转换回String。需要DocumentBuilder实例的组件如果不提供名称空间感知实例,则创建该实例。当需要对文档创建进行更大的控制时,可以提供DocumentBuilder的适当配置实例。

# 名称空间支持

Spring 集成 XML 模块中的所有组件都提供了名称空间支持。为了启用名称空间支持,你需要为 Spring Integration XML 模块导入模式。下面的示例展示了一个典型的设置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-xml="http://www.springframework.org/schema/integration/xml"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/xml
    https://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd">
</beans>

# XPath 表达式

Spring 集成 XML 模块中的许多组件都使用 XPath 表达式。这些组件中的每一个都引用了已定义为顶级元素的 XPath 表达式,或者使用了嵌套的<xpath-expression/>元素。

所有形式的 XPath 表达式都会创建一个XPathExpression,该表达式使用 Spring org.springframework.xml.xpath.XPathExpressionFactory。在创建 XPath 表达式时,将使用 Classpath 上可用的最佳 XPath 实现(JAXP1.3+ 或 JAXen,其中首选 JAXP)。

在内部, Spring 集成使用了 Spring Web 服务项目(https://www.spring.io/spring-ws (opens new window))提供的 XPath 功能。
具体地说,我们使用 Spring Web 服务 XML 模块( Spring-xml-x.x.x.jar)。
有关更深入的理解,请参见https://docs.spring.io/spring-ws/docs/current/reference/#xpath (opens new window)上的相应文档。

下面是xpath-expression元素的所有可用配置参数的概述:下面的清单显示了xpath-expression元素的可用属性:

<int-xml:xpath-expression expression="" (1)
          id=""                         (2)
          namespace-map=""              (3)
          ns-prefix=""                  (4)
          ns-uri="">                    (5)
    <map></map>                         (6)
</int-xml:xpath-expression>
1 定义一个 XPath 表达式。
需要。
2 底层 Bean 定义的标识符。
它是org.springframework.xml.xpath.XPathExpression的实例。
可选的。
3 对包含名称空间的映射的引用。
映射的键定义了名称空间前缀,并且映射的值设置了名称空间 URI。
既指定这个属性又指定map元素或ns-prefixns-uri属性都是无效的。
可选的。
4 让你直接将名称空间前缀设置为 XPath 表达式元素上的一个属性。
如果你设置ns-prefix,还必须设置ns-uri属性。
可选。
5 让你直接将名称空间 URI 设置为 XPath 表达式元素上的一个属性。
如果你设置ns-uri,还必须设置ns-prefix属性。
可选。
6 定义一个包含名称空间的映射。
只允许一个map子元素,
映射的键定义名称空间前缀,并且映射的值设置名称空间 URI。
既指定这个元素又指定map属性,或者设置ns-prefixns-uri属性都是无效的。
可选的。
# 为 XPath 表达式提供名称空间(可选)

对于 XPath 表达式元素,你可以提供名称空间信息作为配置参数。你可以使用以下选项中的一个来定义名称空间:

  • 使用namespace-map属性引用地图

  • 使用map子元素提供名称空间的映射

  • 指定ns-prefixns-uri属性

这三种选择都是相互排斥的。只能设置一个选项。

下面的示例展示了使用 XPath 表达式的几种不同方式,包括设置 XML 名称空间前面提到过的选项:

<int-xml:xpath-filter id="filterReferencingXPathExpression"
                      xpath-expression-ref="refToXpathExpression"/>

<int-xml:xpath-expression id="refToXpathExpression" expression="/name"/>

<int-xml:xpath-filter id="filterWithoutNamespace">
    <int-xml:xpath-expression expression="/name"/>
</int-xml:xpath-filter>

<int-xml:xpath-filter id="filterWithOneNamespace">
    <int-xml:xpath-expression expression="/ns1:name"
                              ns-prefix="ns1" ns-uri="www.example.org"/>
</int-xml:xpath-filter>

<int-xml:xpath-filter id="filterWithTwoNamespaces">
    <int-xml:xpath-expression expression="/ns1:name/ns2:type">
        <map>
            <entry key="ns1" value="www.example.org/one"/>
            <entry key="ns2" value="www.example.org/two"/>
        </map>
    </int-xml:xpath-expression>
</int-xml:xpath-filter>

<int-xml:xpath-filter id="filterWithNamespaceMapReference">
    <int-xml:xpath-expression expression="/ns1:name/ns2:type"
                              namespace-map="defaultNamespaces"/>
</int-xml:xpath-filter>

<util:map id="defaultNamespaces">
    <util:entry key="ns1" value="www.example.org/one"/>
    <util:entry key="ns2" value="www.example.org/two"/>
</util:map>
# 使用带有默认名称空间的 XPath 表达式

在使用默认名称空间时,你可能会遇到与预期的行为不同的情况。假设我们有以下 XML 文档(它代表两本书的顺序):

<?xml version="1.0" encoding="UTF-8"?>
<order>
    <orderItem>
        <isbn>0321200683</isbn>
        <quantity>2</quantity>
    </orderItem>
    <orderItem>
        <isbn>1590596439</isbn>
        <quantity>1</quantity>
    </orderItem>
</order>

该文档不声明名称空间。因此,应用下面的 XPath 表达式如预期的那样工作:

<int-xml:xpath-expression expression="/order/orderItem" />

你可能认为相同的表达式也适用于以下 XML 文件:

<?xml version="1.0" encoding="UTF-8"?>
<order xmlns="http://www.example.org/orders">
	<orderItem>
		<isbn>0321200683</isbn>
		<quantity>2</quantity>
	</orderItem>
	<orderItem>
		<isbn>1590596439</isbn>
		<quantity>1</quantity>
	</orderItem>
</order>

前面的示例看起来与前面的示例完全相同,但声明了一个默认的名称空间。

但是,前面的 XPath 表达式(/order/orderItem)在这种情况下会失败。

为了解决这个问题,你必须通过设置ns-prefixns-uri属性或设置namespace-map属性来提供名称空间前缀和名称空间 URI。名称空间 URI 必须与 XML 文档中声明的名称空间匹配。在前面的示例中,这是[http://www.example.org/orders](http://www.example.org/orders)

但是,你可以任意选择名称空间前缀。实际上,提供一个空字符串实际上是可行的。(但是,不允许使用 NULL)在名称空间前缀由空字符串组成的情况下,你的 XPath 表达式必须使用冒号(“:”)来指示默认的名称空间。如果不使用冒号,则 XPath 表达式不匹配。下面的 XPath 表达式与前面示例中的 XML 文档匹配:

<int-xml:xpath-expression expression="/:order/:orderItem"
    ns-prefix="" ns-uri="https://www.example.org/prodcuts"/>

你还可以提供任何其他任意选择的名称空间前缀。下面的 XPath 表达式(使用myorder名称空间前缀)也匹配:

<int-xml:xpath-expression expression="/myorder:order/myorder:orderItem"
    ns-prefix="myorder" ns-uri="https://www.example.org/prodcuts"/>

名称空间 URI 是真正重要的信息,而不是前缀。这个 https://github.com/jaxen-xpath/jaxen[jaxen]很好地总结了这一点:

在 XPath1.0 中,所有无前缀的名称都是不合格的。 不需要 XPath 表达式中使用的前缀与被查询的文档中使用的前缀相同。 只有名称空间 URI 需要匹配,而不是前缀。

# 转换 XML 有效负载

本节介绍了如何转换 XML 有效负载。

# 将变压器配置为 bean

本节将解释以下转换器的工作方式,以及如何将它们配置为 bean:

所有的 XML 转换器都扩展了[AbstractTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/Transformer/AbstractTransformer.html)或[AbstractPayloadTransformer(https://DOCS. Spring.io/ Spring-integration/api/org/org/integration/AbstractTransformer/AbstractTransformer/AbstractTransformer/AbstractTransformer/AbstractTransformer.html),并因此实现了[](https:////DOCS. Spring-integration/在 Spring 集成中将 XML 转换器配置为 bean 时,通常需要结合[Transformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/Transforminghandler/MessageTransforminghandler.html)配置<gtr="116"/>。这使得变压器可以用作端点。最后,我们讨论了名称空间支持,它允许将转换器配置为 XML 中的元素。

# 解组变压器

[UnmarshallingTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/transformer/unmarshallingtransformer.html)允许使用Spring OXM (opens new window)Unmarshaller的实现来解组 XMLSource。 Spring 的对象/XML 映射支持提供了几种实现,通过使用JAXB (opens new window)Castor (opens new window)JiBX (opens new window)和其他方式支持编组和解组。解组器需要Source的实例。如果消息有效负载不是Source的实例,则仍将尝试转换。目前,StringFilebyte[]org.w3c.dom.Document都支持有效载荷。要创建到Source的自定义转换,可以注入一个[SourceFactory](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/source/sourcefactory.html)的实现。

如果你没有显式地设置SourceFactory,则UnmarshallingTransformer上的属性默认设置为[DomSourceFactory](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/source/domsourceFactory.html)。

从版本 5.0 开始,UnmarshallingTransformer还支持将org.springframework.ws.mime.MimeMessage作为传入负载。当我们通过 SOAP 接收带有 MTOM 附件的 RAWWebServiceMessage时,这可能是有用的。有关更多信息,请参见MTOM 支持

下面的示例展示了如何定义解组转换器:

<bean id="unmarshallingTransformer" class="o.s.i.xml.transformer.UnmarshallingTransformer">
    <constructor-arg>
        <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
            <property name="contextPath" value="org.example" />
        </bean>
    </constructor-arg>
</bean>
# 使用MarshallingTransformer

[MarshallingTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/Transformer/MarshallingTransformer.html)允许通过使用 Spring OXMMarshaller将对象图转换为 XML。默认情况下,MarshallingTransformer返回DomResult。但是,你可以通过配置一个替代选项ResultFactory来控制结果的类型,例如StringResultFactory。在许多情况下,将有效负载转换为可选的 XML 格式更方便。为此,配置ResultTransformer。 Spring 集成提供了两种实现方式,一种转换为String,另一种转换为Document。下面的示例配置了一个可以转换为文档的编组转换器:

<bean id="marshallingTransformer" class="o.s.i.xml.transformer.MarshallingTransformer">
    <constructor-arg>
        <bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
            <property name="contextPath" value="org.example"/>
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean class="o.s.i.xml.transformer.ResultToDocumentTransformer"/>
    </constructor-arg>
</bean>

默认情况下,MarshallingTransformer将有效负载对象传递给Marshaller。但是,如果将其布尔extractPayload属性设置为false,则整个Message实例将被传递给Marshaller。对于Marshaller接口的某些定制实现,这可能是有用的,但是,通常情况下,当你委托给各种Marshaller实现时,有效负载是用于编组的合适的源对象。

# xsltpayloadTransformer

[XsltPayloadTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/transformer/xsltpayloadTransformer.html)通过使用可扩展样式表语言转换 (opens new window)转换 XML 有效负载。Transformer 的构造函数需要传入Resource (opens new window)Templates (opens new window)的实例。传入Templates实例可以更好地配置用于创建模板实例的TransformerFactory实例。

与[UnmarshallingTransformer](#xml-unmarshalling-transformer)一样,XsltPayloadTransformer针对Source的实例执行实际的 XSLT 转换。因此,如果消息有效负载不是Source的实例,则仍然尝试进行转换。StringDocument有效负载是直接支持的。

要创建到Source的自定义转换,你可以注入一个[SourceFactory](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/source/sourcefactory.html)的实现。

如果SourceFactory未显式设置,则XsltPayloadTransformer上的属性默认设置为[DomSourceFactory](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/source/domsourceFactory.html)。

默认情况下,XsltPayloadTransformer创建带有[Result](https://DOCS.oracle.com/javase/6/DOCS/api/javax/xml/transform/result.html)有效负载的消息,类似于XmlPayloadMarshallingTransformer。你可以通过提供[ResultFactory](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/result/resultfactory.html)或[ResultTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/transformer/resultformer.html)来对此进行定制。

下面的示例配置 Bean 作为 XSLT 有效负载转换器工作的 Bean:

<bean id="xsltPayloadTransformer" class="o.s.i.xml.transformer.XsltPayloadTransformer">
  <constructor-arg value="classpath:org/example/xsl/transform.xsl"/>
  <constructor-arg>
    <bean class="o.s.i.xml.transformer.ResultToDocumentTransformer"/>
  </constructor-arg>
</bean>

从 Spring Integration3.0 开始,你可以通过使用构造函数参数来指定 Transformer Factory 类名。在使用名称空间时,可以使用transformer-factory-class属性来实现此目的。

# 使用ResultTransformer实现

MarshallingTransformerXsltPayloadTransformer都允许你指定一个[ResultTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/Transformer/resultTransformer.html)。因此,如果编组或 XSLT 转换返回[Result](https://DOCS.oracle.com/javase/6/DOCS/api/javax/xml/transform/result.html),则你还可以选择使用ResultTransformerResult转换为另一种格式。 Spring 集成提供了两个具体的ResultTransformer实现:

  • [ResultToDocumentTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/Transformer/ResultToDocumentTransformer.html)

  • [ResultToStringTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/Transformer/resultoStringTransformer.html)

默认情况下,MarshallingTransformer总是返回一个[Result](https://DOCS.oracle.com/javase/6/DOCS/api/javax/xml/transform/result.html)。通过指定ResultTransformer,你可以自定义返回的有效负载的类型。

对于XsltPayloadTransformer,行为稍微复杂一些。默认情况下,如果输入有效负载是String或[Document](https://DOCS.oracle.com/javase/6/DOCS/api/org/w3c/dom/document.html)的实例,则resultTransformer属性将被忽略。

但是,如果输入有效负载是[Source](https://DOCS.oracle.com/javase/6/DOCS/api/javax/xml/transform/source.html)或任何其他类型,则应用resultTransformer属性。此外,你可以将alwaysUseResultFactory属性设置为true,这也会导致使用指定的resultTransformer

有关更多信息和示例,请参见名称空间配置和结果转换器

# XML Transformers 的命名空间支持

Spring Integration XML 命名空间提供了对所有 XML 转换器的命名空间支持,其模板为显示在前面。对 Transformers 的命名空间支持根据所提供的输入通道的类型创建EventDrivenConsumerPollingConsumer的实例。名称空间支持旨在通过允许创建使用一个元素的端点和转换器来减少 XML 配置的数量。

# 使用UnmarshallingTransformer

UnmarshallingTransformer的命名空间支持如下所示。由于名称空间创建的是一个端点实例,而不是一个转换器,因此你可以在元素中嵌入一个 poller 来控制输入通道的轮询。下面的示例展示了如何做到这一点:

<int-xml:unmarshalling-transformer id="defaultUnmarshaller"
    input-channel="input" output-channel="output"
    unmarshaller="unmarshaller"/>

<int-xml:unmarshalling-transformer id="unmarshallerWithPoller"
    input-channel="input" output-channel="output"
    unmarshaller="unmarshaller">
    <int:poller fixed-rate="2000"/>
<int-xml:unmarshalling-transformer/>
# 使用MarshallingTransformer

对编组转换器的命名空间支持需要input-channeloutput-channel和对marshaller的引用。你可以使用可选的result-type属性来控制创建的结果类型。有效值为StringResultDomResult(默认值)。下面的示例配置了一个编组转换器:

<int-xml:marshalling-transformer
     input-channel="marshallingTransformerStringResultFactory"
     output-channel="output"
     marshaller="marshaller"
     result-type="StringResult" />

<int-xml:marshalling-transformer
    input-channel="marshallingTransformerWithResultTransformer"
    output-channel="output"
    marshaller="marshaller"
    result-transformer="resultTransformer" />

<bean id="resultTransformer" class="o.s.i.xml.transformer.ResultToStringTransformer"/>

如果提供的结果类型不够,则可以提供对ResultFactory的自定义实现的引用,作为使用result-type属性设置result-type属性的替代方法。result-typeresult-factory属性是互斥的。

在内部,StringResultDomResult结果类型由ResultFactory实现表示:[StringResultFactory](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/result/stringresultfactory.html)和[DomResultFactory(https://DOCS. Spring.io/ Spring-integration/api/api/integration/xml/result/resultfactory.html)。
# 使用XsltPayloadTransformer

XsltPayloadTransformer的命名空间支持允许你传入Resource(以便创建[Templates](https://DOCS.oracle.com/javase/6/DOCS/api/javax/xml/transform/templates.html)实例)或传入预先创建的Templates实例作为参考。与编组转换器一样,你可以通过指定result-factoryresult-type属性来控制结果输出的类型。当需要在发送之前转换结果时,可以使用result-transformer属性来引用ResultTransformer的实现。

如果指定result-factoryresult-type属性,则底层[alwaysUseResultFactory](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/Transformer/xsltpayloadformer.html)上的true属性将被[<<XsltPayloadTransformerParser](https://DOCS. Spring.api/api/api/api/api/api/api63/apylotframFramework/xtframework/xparser.html)设置

下面的示例配置了两个 XSLT 转换器:

<int-xml:xslt-transformer id="xsltTransformerWithResource"
    input-channel="withResourceIn" output-channel="output"
    xsl-resource="org/springframework/integration/xml/config/test.xsl"/>

<int-xml:xslt-transformer id="xsltTransformerWithTemplatesAndResultTransformer"
    input-channel="withTemplatesAndResultTransformerIn" output-channel="output"
    xsl-templates="templates"
    result-transformer="resultTransformer"/>

你可能需要访问Message数据,例如Message头,以便协助转换。例如,你可能需要访问某些Message头,并将它们作为参数传递给转换器(例如,transformer.setParameter(..))。 Spring 集成提供了两种方便的方式来实现这一点,如下例所示:

<int-xml:xslt-transformer id="paramHeadersCombo"
    input-channel="paramHeadersComboChannel" output-channel="output"
    xsl-resource="classpath:transformer.xslt"
    xslt-param-headers="testP*, *foo, bar, baz">

    <int-xml:xslt-param name="helloParameter" value="hello"/>
    <int-xml:xslt-param name="firstName" expression="headers.fname"/>
</int-xml:xslt-transformer>

如果消息头名称与参数名称一一匹配,则可以使用xslt-param-headers属性。在其中,你可以使用通配符进行简单的模式匹配。它支持以下简单的模式样式:xxx***xxx***xxx,和xxx*yyy

你还可以通过使用<xslt-param/>元素来配置各个 XSLT 参数。在该元素上,你可以设置expression属性或value属性。expression属性应该是任何有效的 SPEL 表达式,而Message是表达式求值上下文的根对象。value属性(与 Spring bean 中的任何value一样)允许你指定简单的标量值。你也可以使用属性占位符(例如${some.value})。因此,使用expressionvalue属性,你可以将 XSLT 参数映射到Message的任何可访问部分以及任何文本值。

从 Spring Integration3.0 开始,你现在可以通过设置transformer-factory-class属性来指定 Transformer Factory 类名称。

# 名称空间配置和结果转换器

我们在[使用ResultTransformer实现](#xml-using-result-transformers)中介绍了使用结果转换器的情况。本节中的示例使用 XML 名称空间配置来演示几个特殊的用例。首先,我们定义ResultTransformer,如下例所示:

<beans:bean id="resultToDoc" class="o.s.i.xml.transformer.ResultToDocumentTransformer"/>

ResultTransformer接受StringResultDOMResult作为输入,并将输入转换为Document

现在我们可以申报变压器了,具体如下:

<int-xml:xslt-transformer input-channel="in" output-channel="fahrenheitChannel"
    xsl-resource="classpath:noop.xslt" result-transformer="resultToDoc"/>

如果传入消息的有效负载类型为Source,那么作为第一步,通过使用ResultFactory来确定Result。由于我们没有指定ResultFactory,所以使用了缺省的DomResultFactory,这意味着转换会产生DomResult

然而,当我们指定一个ResultTransformer时,它被使用,结果Message的有效负载类型为Document

指定的ResultTransformer将被StringDocument有效载荷忽略。
如果传入消息的有效载荷类型为String,则 XSLT 转换后的有效载荷为String
类似地,如果传入消息的有效载荷类型为Document,XSLT 转换后的有效负载是Document

如果消息有效负载不是SourceStringDocument,则作为一个后备选项,我们尝试通过使用默认的[Source](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/source/sourcefactory.html)来创建Source。由于我们没有使用source-factory属性显式地指定SourceFactory,因此使用了默认的[DomSourceFactory](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/source/domsourcefactory.html)。如果成功,将执行 XSLT 转换,就好像负载类型Source一样,如前面几段所述。

DomSourceFactory支持从DocumentFileString有效负载创建DOMSource

下一个 Transformer 声明添加了一个result-type属性,该属性使用StringResult作为其值。result-type在内部由StringResultFactory表示。因此,你还可以通过使用result-factory属性添加对StringResultFactory的引用,该属性本来是相同的。下面的示例展示了 Transformer 声明:

<int-xml:xslt-transformer input-channel="in" output-channel="fahrenheitChannel"
		xsl-resource="classpath:noop.xslt" result-transformer="resultToDoc"
		result-type="StringResult"/>

因为我们使用了ResultFactory,所以XsltPayloadTransformer类的alwaysUseResultFactory属性隐式地设置为true。因此,使用了引用的ResultToDocumentTransformer

因此,如果你转换类型为String的有效负载,则生成的有效负载类型为[Document](https://DOCS.oracle.com/javase/6/DOCS/api/org/w3c/dom/document.html)。

# XsltPayloadTransformer<xsl:output method="text"/>

<xsl:output method="text"/>告诉 XSLT 模板仅从输入源生成文本内容。在这种特殊情况下,我们没有理由使用DomResult。因此,[XsltPayloadTransformer](https://DOCS. Spring.io/ Spring-integration/api/org/springframework/integration/xml/transformer/xsltpayloadTransformer.html)默认为StringResult如果[输出属性](https://docs.oracle.com/javase/7/docs/api/javax/xml/transform/Transformer.html#getOutputProperties())调用底层methodjavax.xml.transform.Transformer返回text。这种强制执行独立于入站有效负载类型。只有为<int-xml:xslt-transformer>组件设置 ifresult-type属性或result-factory属性时,此行为才可用。

# 使用 XPath 转换 XML 消息

当涉及到消息转换时,XPath 是转换具有 XML 有效负载的消息的一种很好的方法。你可以通过使用<xpath-transformer/>元素定义 XPath Transformers 来实现此目的。

# 简单的 XPath 转换

考虑以下变压器配置:

<int-xml:xpath-transformer input-channel="inputChannel" output-channel="outputChannel"
      xpath-expression="/person/@name" />

还要考虑以下Message:

Message<?> message =
  MessageBuilder.withPayload("<person name='John Doe' age='42' married='true'/>").build();

在将此消息发送到“InputChannel”之后,先前配置的 XPath Transformer 将此 XML 消息转换为一个简单的Message,其有效负载为’John Doe’,所有这些都基于xpath-expression属性中指定的简单 XPath 表达式。

XPath 还允许你将提取的元素简单地转换为所需的类型。有效的返回类型在javax.xml.xpath.XPathConstants中定义,并遵循javax.xml.xpath.XPath接口指定的转换规则。

下面的常量由XPathConstants类定义:BOOLEANDOM_OBJECT_MODELNODENODESETNUMBER,和STRING

可以通过使用<xpath-transformer/>元素的evaluation-type属性来配置所需的类型,如下例所示(两次):

<int-xml:xpath-transformer input-channel="numberInput" xpath-expression="/person/@age"
                           evaluation-type="NUMBER_RESULT" output-channel="output"/>

<int-xml:xpath-transformer input-channel="booleanInput"
                           xpath-expression="/person/@married = 'true'"
                           evaluation-type="BOOLEAN_RESULT" output-channel="output"/>

# 节点映射器

如果需要为通过 XPath 表达式提取的节点提供自定义映射,则可以提供对org.springframework.xml.xpath.NodeMapper的实现的引用(由XPathOperations使用的接口实现为在每个节点的基础上映射Node对象)。要提供对NodeMapper的引用,可以使用node-mapper属性,如下例所示:

<int-xml:xpath-transformer input-channel="nodeMapperInput" xpath-expression="/person/@age"
                           node-mapper="testNodeMapper" output-channel="output"/>

下面的示例显示了与前面的示例一起工作的NodeMapper实现:

class TestNodeMapper implements NodeMapper {
  public Object mapNode(Node node, int nodeNum) throws DOMException {
    return node.getTextContent() + "-mapped";
  }
}

# XML 有效载荷转换器

你还可以使用org.springframework.integration.xml.XmlPayloadConverter的实现来提供更细粒度的转换。下面的示例展示了如何定义一个:

<int-xml:xpath-transformer input-channel="customConverterInput"
                           output-channel="output" xpath-expression="/test/@type"
                           converter="testXmlPayloadConverter" />

下面的示例显示了与前面的示例一起工作的XmlPayloadConverter实现:

class TestXmlPayloadConverter implements XmlPayloadConverter {
  public Source convertToSource(Object object) {
    throw new UnsupportedOperationException();
  }
  //
  public Node convertToNode(Object object) {
    try {
      return DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(
          new InputSource(new StringReader("<test type='custom'/>")));
    }
    catch (Exception e) {
      throw new IllegalStateException(e);
    }
  }
  //
  public Document convertToDocument(Object object) {
    throw new UnsupportedOperationException();
  }
}

如果不提供此引用,则使用DefaultXmlPayloadConverter。在大多数情况下,它应该足够了,因为它可以从NodeDocumentSourceFileStringInputStreambyte[]有效载荷转换。如果你需要扩展到该默认实现的功能之外,那么上游Transformer可能是一个更好的选择,而不是在这里提供对该策略的自定义实现的引用。

# 分割 XML 消息

XPathMessageSplitter支持带有StringDocument有效负载的消息。拆分器使用提供的 XPath 表达式将有效负载拆分为多个节点。默认情况下,这将导致每个Node实例成为新消息的有效负载。当每条消息应该是Document时,可以设置createDocuments标志。在传入String有效载荷的情况下,将对有效载荷进行转换,然后将其拆分,然后再转换回许多String消息。XPath 拆分器实现MessageHandler,因此应该与适当的端点一起进行配置(请参阅下面示例之后的名称空间支持示例,以获得更简单的配置替代方案)。下面的示例配置使用XPathMessageSplitter的 Bean:

<bean id="splittingEndpoint"
      class="org.springframework.integration.endpoint.EventDrivenConsumer">
    <constructor-arg ref="orderChannel" />
    <constructor-arg>
        <bean class="org.springframework.integration.xml.splitter.XPathMessageSplitter">
            <constructor-arg value="/order/items" />
            <property name="documentBuilder" ref="customisedDocumentBuilder" />
            <property name="outputChannel" ref="orderItemsChannel" />
        </bean>
    </constructor-arg>
</bean>

XPath Splitter 名称空间支持允许你创建带有输入通道和输出通道的消息端点,如下例所示:

<!-- Split the order into items and create a new message for each item node -->
<int-xml:xpath-splitter id="orderItemSplitter"
                       input-channel="orderChannel"
                       output-channel="orderItemsChannel">
    <int-xml:xpath-expression expression="/order/items"/>
</int-xml:xpath-splitter>

<!-- Split the order into items, create a new document for each item-->
<int-xml:xpath-splitter id="orderItemDocumentSplitter"
                       input-channel="orderChannel"
                       output-channel="orderItemsChannel"
                       create-documents="true">
    <int-xml:xpath-expression expression="/order/items"/>
    <int:poller fixed-rate="2000"/>
</int-xml:xpath-splitter>

从版本 4.2 开始,当请求payload类型不是org.w3c.dom.Node时,outputProperties实例的OutputKeys.OMIT_XML_DECLARATION属性(例如OutputKeys.OMIT_XML_DECLARATION)公开。下面的示例定义了一个属性,并将其与output-properties属性一起使用:

<util:properties id="outputProperties">
	<beans:prop key="#{T (javax.xml.transform.OutputKeys).OMIT_XML_DECLARATION}">yes</beans:prop>
</util:properties>

<xpath-splitter input-channel="input"
             output-properties="outputProperties">
    <xpath-expression expression="/orders/order"/>
</xpath-splitter>

version 4.2开始,XPathMessageSplitter将一个iterator选项公开为boolean标志(默认为true)。这允许下游流中分割节点的“流”。通过将iterator模式设置为true,每个节点在迭代时进行转换。当false时,首先对所有条目进行转换,然后将分割的节点开始发送到输出通道。(你可以将其视为“transform,send,transform,send,send”与“transform,transform,send,send”的区别。)有关更多信息,请参见Splitter

# 使用 XPath 路由 XML 消息

Spring 与基于 SPEL 的路由器类似,集成提供了对基于 XPath 表达式的路由消息的支持,它允许你创建具有输入通道但没有输出通道的消息端点。相反,一个或多个输出通道是动态确定的。下面的示例展示了如何创建这样的路由器:

<int-xml:xpath-router id="orderTypeRouter" input-channel="orderChannel">
    <int-xml:xpath-expression expression="/order/type"/>
</int-xml:xpath-router>
有关路由器中常见的属性的概述,请参见通用路由器参数

在内部,XPath 表达式被求值为类型NODESET,并转换为表示通道名的List<String>。通常,这样的列表只包含一个通道名。然而,基于 XPath 表达式的结果,如果 XPath 表达式返回多个值,则 XPath 路由器还可以具有收件人列表路由器的特性。在这种情况下,List<String>包含多个通道名。因此,消息被发送到列表中的所有通道。

因此,假设传递给以下路由器配置的 XML 文件包含许多表示通道名的responder子元素,则消息将被发送到所有这些通道:

<!-- route the order to all responders-->
<int-xml:xpath-router id="responderRouter" input-channel="orderChannel">
    <int-xml:xpath-expression expression="/request/responders"/>
</int-xml:xpath-router>

如果返回的值不直接表示通道名称,则可以指定其他映射参数,将这些返回的值映射到实际的通道名称。例如,如果/request/responders表达式导致两个值(responderAresponderB),但不希望将响应者名称与通道名称耦合,则可以提供额外的映射配置,例如:

<!-- route the order to all responders-->
<int-xml:xpath-router id="responderRouter" input-channel="orderChannel">
    <int-xml:xpath-expression expression="/request/responders"/>
    <int-xml:mapping value="responderA" channel="channelA"/>
    <int-xml:mapping value="responderB" channel="channelB"/>
</int-xml:xpath-router>

如前所述,XPath 表达式的默认求值类型是NODESET,它被转换为通道名的List<String>,它处理单个通道场景以及多个通道场景。

尽管如此,某些 XPath 表达式从一开始就可以计算为类型String。例如,考虑下面的 XPath 表达式:

name(./node())

这个表达式返回根节点的名称。如果使用了默认的求值类型NODESET,则会导致异常。

对于这些场景,你可以使用evaluate-as-string属性,该属性允许你管理求值类型。默认情况下是FALSE。但是,如果将其设置为TRUE,则使用String求值类型。

XPath1.0 指定了 4 种数据类型:

*node-sets

*strings
*number
boolean当使用可选路径
时,返回值由string()函数求值,正如 XPath 规范中所定义的。
这意味着,如果表达式选择了多个节点,它将返回第一个节点的字符串值。

有关更多信息,请参见:
规范:XMLPath 语言 1.0 版本 (opens new window)
XPath 规范-string()函数 (opens new window)

例如,如果我们想基于根节点的名称进行路由,我们可以使用以下配置:

<int-xml:xpath-router id="xpathRouterAsString"
        input-channel="xpathStringChannel"
        evaluate-as-string="true">
    <int-xml:xpath-expression expression="name(./node())"/>
</int-xml:xpath-router>

# XML 有效负载转换器

对于 XPath 路由器,你还可以在 XPath 评估之前指定转换有效负载时使用的转换器。因此,XPath 路由器支持XmlPayloadConverter策略的自定义实现,并且当在 XML 中配置xpath-router元素时,可以通过converter属性提供对这种实现的引用。

如果没有显式提供此引用,则使用DefaultXmlPayloadConverter。在大多数情况下,它应该足够了,因为它可以从节点、文档、源、文件和字符串类型的有效负载转换。如果你需要扩展到该默认实现的能力之外,那么在大多数情况下,上游转换器通常是更好的选择,而不是在这里提供对此策略的自定义实现的引用。

# XPath Header Enricher

XPath Header Enricher 定义了 Header Enricher 消息转换器,该消息转换器根据消息有效负载计算 XPath 表达式,并将计算结果插入到消息头中。

下面的清单显示了所有可用的配置参数:

<int-xml:xpath-header-enricher default-overwrite="true"    (1)
                               id=""                       (2)
                               input-channel=""            (3)
                               output-channel=""           (4)
                               should-skip-nulls="true">   (5)
    <int:poller></int:poller>                              (6)
    <int-xml:header name=""                                (7)
                    evaluation-type="STRING_RESULT"        (8)
                    header-type="int"                      (9)
                    overwrite="true"                       (10)
                    xpath-expression=""                    (11)
                    xpath-expression-ref=""/>              (12)
</int-xml:xpath-header-enricher>
1 指定是否覆盖现有标头值的默认布尔值。
这仅对不提供自己的“覆盖”属性的子元素有效,
如果不设置“默认-覆盖”属性,指定的标头值不会覆盖任何具有相同标头名的现有标头值。
可选。
2 ID 为底层的 Bean 定义。
可选的。
3 这个端点的接收消息通道。
可选的。
4 丰富的消息被发送到的通道。
可选。
5 指定是否应跳过空值,例如可能从表达式求值返回的空值。
默认值是true
如果空值应触发删除相应的标头,请将其设置为false
可选。
6 与标题 Enricher 一起使用的 Poller。
可选的。
7 要丰富的标题的名称。
强制。
8 如果你没有设置header-type属性,则这是该标头值的类型。
允许以下值:BOOLEAN_RESULTSTRING_RESULTNUMBER_RESULTNODE_RESULT,以及NODE_LIST_RESULT
如果未设置,它在内部默认为XPathEvaluationType.STRING_RESULT
可选。
9 标头值类型的完全限定类名称。
XPath 求值的结果通过ConversionService转换为该类型。
例如,这允许,将NUMBER_RESULT(一个 double)转换为Integer
该类型可以声明为原语(例如int),但结果总是等价的包装类(例如Integer)。
中讨论的相同的积分ConversionService用于转换,因此通过向服务中添加一个自定义转换器可以支持到自定义类型的转换。
可选。
10 布尔值,用于指示如果输入Message中已经存在,则此头标值是否应覆盖相同名称的现有头标值。
11 XPath 表达式为String
必须设置这个属性或xpath-expression-ref,但不能同时设置这两个属性。
12 XPath 表达式引用.
必须设置这个属性或xpath-expression,但不能同时设置这两个属性。

# 使用 XPath 过滤器

这个组件定义了一个基于 XPath 的消息过滤器。在内部,这个组件使用MessageFilter来包装AbstractXPathMessageSelector的实例。

有关更多详细信息,请参见Filter

要使用 XPath 过滤器,你至少必须通过声明xpath-expression元素或在xpath-expression-ref属性中引用一个 XPath 表达式来提供一个 XPath 表达式。

如果提供的 XPath 表达式计算为boolean值,则不需要进一步的配置参数。但是,如果 XPath 表达式的求值为String,则应该设置match-value属性,并与求值结果进行匹配。

match-type有三种选择:

  • exact:在java.lang.String上对应于equals。底层实现使用StringValueTestXPathMessageSelector

  • case-insensitive:在java.lang.String上对应于equals-ignore-case。底层实现使用StringValueTestXPathMessageSelector

  • regex:匹配操作 1java.lang.String。底层实现使用RegexTestXPathMessageSelector

当提供“regex”的“match-type”值时,带有match-value属性的值必须是有效的正则表达式。

下面的示例显示了xpath-filter元素的所有可用属性:

<int-xml:xpath-filter discard-channel=""                      (1)
                      id=""                                   (2)
                      input-channel=""                        (3)
                      match-type="exact"                      (4)
                      match-value=""                          (5)
                      output-channel=""                       (6)
                      throw-exception-on-rejection="false"    (7)
                      xpath-expression-ref="">                (8)
    <int-xml:xpath-expression ... />                          (9)
    <int:poller ... />                                        (10)
</int-xml:xpath-filter>
1 要发送被拒绝的消息的消息通道。
可选的。
2 ID 为底层的 Bean 定义。
可选的。
3 这个端点的接收消息通道。
可选。
4 要在 XPath 求值结果和match-value之间应用匹配的类型。
默认值是exact
可选的。
5 要与 XPath 求值结果匹配的字符串值。
如果不设置此属性,则 XPath 求值必须产生一个布尔结果。
可选。
6 将与筛选条件匹配的消息发送到的通道。
可选。
7 默认情况下,此属性被设置为false,并且被拒绝的消息(与筛选条件不匹配的消息)将被静默删除。
但是,如果设置为true,则消息拒绝将导致一个错误条件,并且异常将向上传播到调用者。
可选。
8 引用 XPath 表达式实例进行求值。
9 此子元素设置要求值的 XPath 表达式。
如果不包含此元素,则必须设置xpath-expression-ref属性。
此外,你只能包含一个xpath-expression元素。
10 与 XPath 过滤器一起使用的 Poller。
可选的。

# #XPath spel 函数

Spring 集成,自版本 3.0 以来,提供了内置的#xpathspel 函数,它调用XPathUtils.evaluate(…​)静态方法。此方法将委托给org.springframework.xml.xpath.XPathExpression。下面的清单展示了一些使用示例:

<transformer expression="#xpath(payload, '/name')"/>

<filter expression="#xpath(payload, headers.xpath, 'boolean')"/>

<splitter expression="#xpath(payload, '//book', 'document_list')"/>

<router expression="#xpath(payload, '/person/@age', 'number')">
    <mapping channel="output1" value="16"/>
    <mapping channel="output2" value="45"/>
</router>

#xpath()还支持用于转换 XPath 求值结果的第三个可选参数。它可以是字符串常量之一(stringbooleannumbernodenode_listdocument_list)或org.springframework.xml.xpath.NodeMapper实例。默认情况下,#xpathspel 函数返回 XPath 求值的String表示。

要启用#xpathspel 函数,你可以将spring-integration-xml.jar添加到 Classpath。
你不需要从 Spring 集成 XML 命名空间中声明任何组件。

有关更多信息,请参见“`Spring Expression Language (SpEL)

# XML 验证过滤器

XML 验证过滤器允许你根据提供的模式实例验证传入消息。支持以下模式类型:

验证失败的消息可以被静默删除,也可以转发到可定义的discard-channel。此外,你可以将此筛选器配置为在验证失败的情况下抛出Exception

下面的清单显示了所有可用的配置参数:

<int-xml:validating-filter discard-channel=""                    (1)
                           id=""                                 (2)
                           input-channel=""                      (3)
                           output-channel=""                     (4)
                           schema-location=""                    (5)
                           schema-type="xml-schema"              (6)
                           throw-exception-on-rejection="false"  (7)
                           xml-converter=""                      (8)
                           xml-validator="">                     (9)
    <int:poller .../>                                            (10)
</int-xml:validating-filter>
1 要发送被拒绝的消息的消息通道。
可选的。
2 ID 为底层的 Bean 定义。
可选的。
3 这个端点的接收消息通道。
可选的。
4 要发送已接受的消息的消息通道。
可选的。
5 设置模式的位置以验证消息的有效负载。
内部使用org.springframework.core.io.Resource接口。
你可以设置此属性或xml-validator属性,但不能同时设置这两个属性。
可选。
6 设置模式类型。
可以是xml-schemarelax-ng
可选的。
如果未设置,它默认为xml-schema,在内部翻译为org.springframework.xml.validation.XmlValidatorFactory#SCHEMA_W3C_XML
7 如果true,如果对所提供的消息的有效负载的验证失败,则抛出一个MessageRejectedException
如果未设置,则默认为false
可选。
8 参考自定义org.springframework.integration.xml.XmlPayloadConverter策略。
可选策略。
9 引用自定义的sorg.springframework.xml.validation.XmlValidator策略。
可以设置此属性或schema-location属性,但不能同时设置这两个属性。
可选。
10 与 XPath 过滤器一起使用的 Poller。
可选的。