转换 XML 负载
本节介绍如何转换 XML 负载
配置转换器为Bean
本节将解释以下转换器的工作原理以及如何将它们配置为Bean:
所有 XML 转换器都继承自 AbstractTransformer 或 AbstractPayloadTransformer,因此实现了 Transformer 接口。在 Spring Integration 中将 XML 转换器配置为 Bean 时,通常会将 Transformer 与 MessageTransformingHandler 结合配置,从而使转换器能够作为端点使用。最后,我们将讨论命名空间支持,它允许将转换器配置为 XML 中的元素。
UnmarshallingTransformer
UnmarshallingTransformer 允许使用 Spring OXM Unmarshaller 的实现来解组 XML Source。Spring 的对象/XML 映射支持提供了多种实现,支持使用 JAXB、Castor、JiBX 等进行编组和解组。解组器需要一个 Source 的实例。如果消息负载不是 Source 的实例,仍会尝试进行转换。目前支持 String、File、byte[] 和 org.w3c.dom.Document 类型的负载。要创建自定义的 Source 转换,您可以注入 SourceFactory 的实现。
如果您没有显式设置 SourceFactory,默认情况下,UnmarshallingTransformer 上的属性会被设置为 DomSourceFactory。
从 5.0 版本开始,UnmarshallingTransformer 也支持将 org.springframework.ws.mime.MimeMessage 作为传入的有效载荷。当我们通过 SOAP 接收到带有 MTOM 附件的原始 WebServiceMessage 时,这会很有用。更多信息请参阅 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 允许通过使用 Spring OXM Marshaller 将对象图转换为 XML。默认情况下,MarshallingTransformer 返回一个 DomResult。但是,您可以通过配置替代的 ResultFactory(例如 StringResultFactory)来控制结果的类型。在许多情况下,将有效负载转换为替代的 XML 格式更为方便。为此,请配置一个 ResultTransformer。Spring Integration 提供了两种实现:一种转换为 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 通过使用 可扩展样式表语言转换 (XSLT) 来转换 XML 负载。该转换器的构造函数需要传入一个 Resource 或 Templates 的实例。传入一个 Templates 实例允许对用于创建模板实例的 TransformerFactory 进行更详细的配置。
与 UnmarshallingTransformer 类似,XsltPayloadTransformer 对 Source 实例执行实际的 XSLT 转换。因此,即使消息负载不是 Source 实例,系统仍会尝试进行转换。String 和 Document 类型的负载可直接支持。
要创建自定义转换到 Source,你可以注入一个 SourceFactory 的实现。
如果未显式设置 SourceFactory,默认情况下,XsltPayloadTransformer 上的属性将设置为 DomSourceFactory。
默认情况下,XsltPayloadTransformer 会创建一个包含 Result 负载的消息,这与 XmlPayloadMarshallingTransformer 类似。您可以通过提供 ResultFactory 或 ResultTransformer 来自定义此行为。
以下示例配置了一个用作 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 工厂类名。在使用命名空间时,你可以通过 transformer-factory-class 属性来实现这一点。
使用 ResultTransformer 实现
MarshallingTransformer 和 XsltPayloadTransformer 都允许您指定一个 ResultTransformer。因此,如果编组或 XSLT 转换返回一个 Result,您可以选择同时使用 ResultTransformer 将 Result 转换为另一种格式。Spring Integration 提供了两种具体的 ResultTransformer 实现:
默认情况下,MarshallingTransformer 始终返回一个 Result 对象。通过指定 ResultTransformer,您可以自定义返回的负载类型。
对于 XsltPayloadTransformer 来说,其行为稍微复杂一些。默认情况下,如果输入负载是 String 或 Document 的实例,那么 resultTransformer 属性将被忽略。
然而,如果输入负载是 Source 或任何其他类型,则会应用 resultTransformer 属性。此外,您可以将 alwaysUseResultFactory 属性设置为 true,这同样会导致使用指定的 resultTransformer。
更多信息和示例,请参阅命名空间配置与结果转换器。
XML 转换器的命名空间支持
Spring Integration XML命名空间为所有XML转换器提供了命名空间支持,其模板已在先前展示。转换器的命名空间支持会根据所提供输入通道的类型,创建EventDrivenConsumer或PollingConsumer的实例。该命名空间支持旨在通过允许使用单个元素创建端点及转换器,从而减少XML配置量。
使用 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/>
使用 MarshallingTransformer
marshalling 转换器的命名空间支持需要一个 input-channel、一个 output-channel 以及对 marshaller 的引用。你可以使用可选的 result-type 属性来控制创建的结果类型。有效值为 StringResult 或 DomResult(默认值)。以下示例配置了一个 marshalling 转换器:
<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"/>
当提供的返回类型不满足需求时,您可以通过 result-factory 属性引用自定义的 ResultFactory 实现,作为设置 result-type 属性的替代方案。result-type 与 result-factory 属性不可同时使用。
在内部,StringResult 和 DomResult 结果类型分别由以下 ResultFactory 实现表示:StringResultFactory 和 DomResultFactory。
使用 XsltPayloadTransformer
XsltPayloadTransformer 的命名空间支持允许您传入 Resource(用于创建 Templates 实例)或传入预创建的 Templates 实例作为引用。与编组转换器类似,您可以通过指定 result-factory 或 result-type 属性来控制结果输出的类型。当需要在发送前转换结果时,可以使用 result-transformer 属性来引用 ResultTransformer 的实现。
如果你指定了 result-factory 或 result-type 属性,底层的 XsltPayloadTransformer 上的 alwaysUseResultFactory 属性会被 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**、*xxx 和 xxx*yyy。
您还可以通过使用 <xslt-param/> 元素来配置单独的 XSLT 参数。在该元素上,您可以设置 expression 属性或 value 属性。expression 属性应为任何有效的 SpEL 表达式,其中 Message 是表达式评估上下文的根对象。value 属性(与 Spring bean 中的任何 value 一样)允许您指定简单的标量值。您还可以使用属性占位符(例如 ${some.value})。因此,通过 expression 和 value 属性,您可以将 XSLT 参数映射到 Message 的任何可访问部分以及任何字面值。
从 Spring Integration 3.0 开始,现在可以通过设置 transformer-factory-class 属性来指定转换器工厂类名。
命名空间配置与结果转换器
我们在使用 ResultTransformer 实现中介绍了结果转换器的使用。本节中的示例使用 XML 命名空间配置来说明几种特殊用例。首先,我们定义 ResultTransformer,如下例所示:
<beans:bean id="resultToDoc" class="o.s.i.xml.transformer.ResultToDocumentTransformer"/>
这个 ResultTransformer 接受 StringResult 或 DOMResult 作为输入,并将输入转换为 Document。
现在我们可以如下声明 transformer:
<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 类型。
如果消息负载不是 Source、String 或 Document,作为备选方案,我们会尝试使用默认的 SourceFactory 创建一个 Source。由于我们没有通过 source-factory 属性显式指定 SourceFactory,因此会使用默认的 DomSourceFactory。如果成功,XSLT 转换将按照前文所述的方式执行,就像负载是 Source 类型一样。
DomSourceFactory 支持从 Document、File 或 String 负载创建 DOMSource。
下一个转换器声明添加了一个 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 与 <xsl:output method="text"/>
<xsl:output method="text"/> 指示 XSLT 模板仅从输入源生成文本内容。在此特定情况下,我们没有理由使用 DomResult。因此,如果底层 javax.xml.transform.Transformer 的名为 method 的 输出属性 返回 text,则 XsltPayloadTransformer 默认使用 StringResult。无论入站负载类型如何,都会独立执行此强制转换。仅当为 <int-xml:xslt-transformer> 组件设置了 result-type 属性或 result-factory 属性时,此行为才可用。