Transforming XML Payloads

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

Configuring Transformers as Beans

本节将解释以下转换器的作用以及如何将其配置为 bean:

所有 XML 转换器都扩展 AbstractTransformerAbstractPayloadTransformer,因此实现了 Transformer。在 Spring Integration 中将 XML 转换器配置为 bean 时,通常会将 TransformerMessageTransformingHandler 一起配置。这让转换器能够用作端点。最后,我们将讨论命名空间支持,它允许将转换器配置为 XML 中的元素。

UnmarshallingTransformer

UnmarshallingTransformer 允许使用 Spring OXM Unmarshaller 的实现对 XML Source 进行非编组。Spring 的 Object/XML Mapping 支持提供了一些实现,支持使用 JAXBCastorJiBX 等进行编组和非编组。解编组器需要 Source 的实例。如果消息有效负载不是 Source 的实例,仍然会尝试转换。目前,支持 StringFilebyte[]org.w3c.dom.Document 有效负载。若要创建到 Source 的自定义转换,可以注入 SourceFactory 的实现。

如果您没有明确设置 SourceFactory,则`UnmarshallingTransformer`上的属性默认设置为 link:https://docs.spring.io/spring-integration/api/org/springframework/integration/xml/source/DomSourceFactory.html[DomSourceFactory

从 5.0 版本开始,UnmarshallingTransformer 还支持 org.springframework.ws.mime.MimeMessage 作为传入有效负载。当我们收到带有 SOAP 的 MTOM 附件的原始 WebServiceMessage 时,这可能有用。有关详细信息,请参见 MTOM Support

以下示例显示了如何定义一个取消编组转换器:

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

Using MarshallingTransformer

MarshallingTransformer 允许使用 Spring OXM Marshaller 将对象图转换成 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 使用 Extensible Stylesheet Language Transformations(XSLT)转换 XML 有效负载。转换器的构造函数需要传递 ResourceTemplates 实例。传递 Templates 实例允许对用于创建模板实例的 TransformerFactory 进行更高级别的配置。

UnmarshallingTransformer 一样,XsltPayloadTransformerSource 实例执行实际 XSLT 转换。因此,如果消息有效负载不是 Source 的实例,系统仍会尝试转换。直接支持 StringDocument 有效负载。

若要创建到 Source 的自定义转换,可以注入 SourceFactory 的实现。

如果未明确设置`SourceFactory`,则`XsltPayloadTransformer`上的属性默认设置为 link:https://docs.spring.io/spring-integration/api/org/springframework/integration/xml/source/DomSourceFactory.html[DomSourceFactory

默认情况下,XsltPayloadTransformer 会创建一条包含 Result 有效负载的消息,类似于 XmlPayloadMarshallingTransformer。可以通过提供 ResultFactoryResultTransformer 来自定义此消息。

以下示例配置了一个作为 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 Integration 3.0 开始,可以通过使用构造函数参数指定变换器工厂类名。使用命名空间时,可以通过使用 transformer-factory-class 属性来执行此操作。

Using ResultTransformer Implementations

MarshallingTransformerXsltPayloadTransformer 都允许您指定 ResultTransformer。因此,如果编组或 XSLT 转换返回 Result,您可以选择还使用 ResultTransformerResult 转换为另一种格式。Spring Integration 提供了两个具体的 ResultTransformer 实现:

默认情况下,MarshallingTransformer 始终返回 Result。通过指定 ResultTransformer,您可以自定义返回的有效负载类型。

对于 XsltPayloadTransformer,该行为稍微复杂一些。默认情况下,如果输入有效负载是 StringDocument 的实例,则将忽略 resultTransformer 属性。

但是,如果输入有效负载是 Source 或任何其他类型,则将应用 resultTransformer 属性。此外,您可以将 alwaysUseResultFactory 属性设置为 true,这还会导致使用指定的 resultTransformer

有关更多信息和示例,请参阅 Namespace Configuration and Result Transformers

Namespace Support for XML Transformers

Spring Integration XML 命名空间中提供了对所有 XML 变换器的命名空间支持,该命名空间的模板前面已经[显示,xpath-namespace-support]。针对变换器的命名空间支持会根据提供的输入通道的类型创建一个 EventDrivenConsumerPollingConsumer 实例。命名空间支持旨在通过允许创建端点和使用一个元素的变换器来减少 XML 配置量。

Using an UnmarshallingTransformer

UnmarshallingTransformer 的命名空间支持如下所示。由于命名空间创建端点实例而不是变换器,因此可以将轮询器嵌套在元素中以控制对输入通道的轮询。以下示例展示了如何执行此操作:

<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/>

Using a 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-factory 属性。result-typeresult-factory 属性是互斥的。

在内部,StringResult`和`DomResult`结果类型分别由以下实现表示: link:https://docs.spring.io/spring-integration/api/org/springframework/integration/xml/result/StringResultFactory.html[`StringResultFactory`和 link:https://docs.spring.io/spring-integration/api/org/springframework/integration/xml/result/DomResultFactory.html[`DomResultFactory

Using an XsltPayloadTransformer

XsltPayloadTransformer 的命名空间支持允许您传入 Resource(以创建 Templates 实例)或作为引用传入一个预先创建的 Templates 实例。与编组转换器一样,您可以通过指定 result-factoryresult-type 属性来控制结果输出的类型。在发送之前需要转换结果时,可以使用 result-transformer 属性来引用 ResultTransformer 的实现。

如果您指定 result-factory`或`result-type`属性,`alwaysUseResultFactory`在底层 link:https://docs.spring.io/spring-integration/api/org/springframework/integration/xml/transformer/XsltPayloadTransformer.html[`XsltPayloadTransformer`上的属性由 link:https://docs.spring.io/spring-integration/api/org/springframework/integration/xml/config/XsltPayloadTransformerParser.html[`XsltPayloadTransformerParser`设置为`true

以下示例配置了两个 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 Integration 提供了两种方便的方法来实现此目的,如下面的示例所示:

<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, *xxxxxx*yyy

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

从 Spring Integration 3.0 开始,现在可以通过设置 transformer-factory-class 属性来指定变换器工厂类名。

Namespace Configuration and Result Transformers

我们在 Using ResultTransformer Implementations 中介绍了结果转换器的使用方法。本部分中的示例使用 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`时,String`或`Document`有效负载会将其忽略。如果传入消息的有效负载类型为`String,则 XSLT 转换后的有效负载为`String`。同样,如果传入消息的有效负载类型为`Document`,则 XSLT 转换后的有效负载为`Document`。

如果消息负载不是 SourceString`或 `Document,作为备选方案,我们会尝试使用默认的 SourceFactory 来创建一个“Source”。由于我们没有使用 source-factory 属性显式指定 SourceFactory,因此使用了默认的 DomSourceFactory。如果成功,则 XSLT 转换的执行就像负载的类型为 Source,如前几段所述。

DomSourceFactory`支持从`DOMSourceFile`或`String`有效负载创建`Document

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

<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

[[xsltpayloadtransformer-and-<xsl:output-method=-text-/>]]=== XsltPayloadTransformer`和 `<xsl:output method="text"/>

<xsl:output method="text"/> 告诉 XSLT 模板仅从输入源生成文本内容。在这种特殊情况下,我们没有理由使用 DomResult。因此,如果叫做 methodjavax.xml.transform.Transformeroutput property 返回 text,则 XsltPayloadTransformer 默认为 StringResult。此强制转换独立于入站负载类型执行。此行为只有在您为 <int-xml:xslt-transformer> 组件设置了 result-type 属性或 result-factory 属性时才可用。