动态XSLT转换

5
我有一个简单的XML请求,需要转换为以下带有正确命名空间的SOAP消息。在传入的XML请求中,命名空间没有出现,因此在形成SOAP消息时,我们需要注意命名空间。是否有任何XSLT代码片段可以帮助我实现这一点。 注意 - 我们需要像“GetImageRequest”这样基于元素动态执行此XSLT转换,因此需要构建命名空间。(可能我们可以将所有命名空间保存在一个XML文件中,并需要构建SOAP消息)
<request>
<payload>
<GetImageRequest>
   <participantId>1191152220010</participantId>
   <participantCode>131029</participantCode>
   <groupCode>027198</groupCode>
   <userType>EE</userType>
   <clientName>Test</clientName>
   <shoeboxID>123444</shoeboxID>
   <imageID>45235</imageID>
</GetImageRequest>
</payload>
</request>

================== 需要使用正确的命名空间构造以下SOAP消息。

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header>

   </soapenv:Header>
   <soapenv:Body>
      <get:GetShoeboxItemRequest xmlns:get="urn:webservice/server/mobile/shoebox/types/v1/GetShoeboxItem">
         <get:participantId>1060687620010</get:participantId>
         <get:participantCode>1060687620010</get:participantCode>
         <get:groupCode>027198</get:groupCode>
         <get:userType>EE</get:userType>
         <get:clientName>Test</get:clientName>
         <get:shoeboxID>123444</get:shoeboxID>
      </get:GetShoeboxItemRequest>
   </soapenv:Body>
</soapenv:Envelope>

需要快速帮助。 XSLT 代码片段将有所帮助。


2
根据此元素需要构建名称空间。这并不清楚。请提供一个确切的构建命名空间的规则。我无法理解 GetImageRequest 如何变成 "urn:webservice/server/mobile/shoebox/types/v1/GetShoeboxItem"。---另外,您的其他问题得到了回答吗? - michael.hor257k
2个回答

4

这个转换:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pRequestedItemName" select="'Shoebox'"/>

 <xsl:variable name="vUpper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
 <xsl:variable name="vLower" select="'abcdefghijklmnopqrstuvwxyz'"/>

 <xsl:variable name="vLcItemName" select=
                "translate($pRequestedItemName, $vUpper, $vLower)"/>

 <xsl:variable name="vDynNamespace" select=
   "concat('urn:webservice/server/mobile/', $vLcItemName, '/types/v1/Get', 'Item')"/>

  <xsl:template match="/">
        <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
            <soapenv:Header>
            </soapenv:Header>
            <soapenv:Body>
              <xsl:element name="get:Get{$pRequestedItemName}ItemRequest" 
                   namespace="{$vDynNamespace}">
                <xsl:apply-templates select="/*/*/GetImageRequest/node()"/>
              </xsl:element>
            </soapenv:Body>
        </soapenv:Envelope>
  </xsl:template>

  <xsl:template match="*">
    <xsl:element name="get:{name()}" namespace="{$vDynNamespace}">
      <xsl:copy-of select="namespace::*|@*"/>
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

应用于提供的源XML文档时:
<request>
    <payload>
        <GetImageRequest>
            <participantId>1191152220010</participantId>
            <participantCode>131029</participantCode>
            <groupCode>027198</groupCode>
            <userType>EE</userType>
            <clientName>Test</clientName>
            <shoeboxID>123444</shoeboxID>
            <imageID>45235</imageID>
        </GetImageRequest>
    </payload>
</request>

能产生所需的正确结果:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
   <soapenv:Header/>
   <soapenv:Body>
      <get:GetShoeboxItemRequest
         xmlns:get="urn:webservice/server/mobile/shoebox/types/v1/GetItem">
         <get:participantId>1191152220010</get:participantId>
         <get:participantCode>131029</get:participantCode>
         <get:groupCode>027198</get:groupCode>
         <get:userType>EE</get:userType>
         <get:clientName>Test</get:clientName>
         <get:shoeboxID>123444</get:shoeboxID>
         <get:imageID>45235</get:imageID>
      </get:GetShoeboxItemRequest>
   </soapenv:Body>
</soapenv:Envelope>

解释:

  1. 使用了 <xsl:element> 指令的全部功能。
  2. 使用了AVT (属性值模板)。
  3. 动态命名空间中唯一可变的部分应由转换调用者提供为全局参数

如果您需要构建完全动态的命名空间(例如由转换调用者作为全局参数传递),请参见此答案


更新:

在评论中,原始问题澄清为可以将所有请求特定的命名空间收集到一个单独的 XML 文档或全局参数中。

这是解决这个澄清后的问题的方法:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pRequestData">
   <r name="GetImageRequest" 
    ns="urn:webservice/server/mobile/shoebox/types/v1/GetShoeboxItem"/>
   <r name="SaveShoeBoxitemRequest" 
    ns="urn:webservice/server/mobile/shoebox/types/v1/SaveShoeboxItem"/>
   <r name="SaveClaimWithReceiptRequest" 
    ns="urn:webservice/server/mobile/shoebox/types/v1/SaveClaimAndReceipt"/>
   <r name="GetThumbNailImageRequest" 
    ns="urn:webservice/server/mobile/shoebox/types/v1/GetThumbnail"/>
   <r name="AttachReceiptwithExistingClaimRequest" 
    ns="urn:webservice/server/mobile/shoebox/types/v1/AttachClaimAndReceipt"/>
 </xsl:param>

 <xsl:variable name="vParams" select="document('')/*/xsl:param[@name='pRequestData']"/>

  <xsl:template match="/">
        <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
            <soapenv:Header>
            </soapenv:Header>
            <soapenv:Body>
                <xsl:apply-templates select="/*/*/*"/>
            </soapenv:Body>
        </soapenv:Envelope>
  </xsl:template>

  <xsl:template match="*">
    <xsl:param name="pKey" select="local-name()"/>
    <xsl:element name="get:{local-name()}" namespace="{$vParams/*[@name = $pKey]/@ns}">
      <xsl:copy-of select="namespace::*|@*"/>
      <xsl:apply-templates>
        <xsl:with-param name="pKey" select="$pKey"/>
      </xsl:apply-templates>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

当对以下XML文档应用此变换(其中提供了一个具有不同名称的payload子级):

<request>
    <payload>
        <GetImageRequest>
            <participantId>1191152220010</participantId>
            <participantCode>131029</participantCode>
            <groupCode>027198</groupCode>
            <userType>EE</userType>
            <clientName>Test</clientName>
            <shoeboxID>123444</shoeboxID>
            <imageID>45235</imageID>
        </GetImageRequest>
        <SaveShoeBoxitemRequest>
            <participantId>1191152220010</participantId>
            <participantCode>131029</participantCode>
            <groupCode>027198</groupCode>
            <userType>EE</userType>
            <clientName>Test</clientName>
            <shoeboxID>123444</shoeboxID>
            <imageID>45235</imageID>
        </SaveShoeBoxitemRequest>
    </payload>
</request>

所需的正确结果(与其他答案中的“解决方案”不同)已生成:

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
       <soapenv:Header/>
       <soapenv:Body>
          <get:GetImageRequest 
xmlns:get="urn:webservice/server/mobile/shoebox/types/v1/GetShoeboxItem">
             <get:participantId>1191152220010</get:participantId>
             <get:participantCode>131029</get:participantCode>
             <get:groupCode>027198</get:groupCode>
             <get:userType>EE</get:userType>
             <get:clientName>Test</get:clientName>
             <get:shoeboxID>123444</get:shoeboxID>
             <get:imageID>45235</get:imageID>
          </get:GetImageRequest>
          <get:SaveShoeBoxitemRequest 
xmlns:get="urn:webservice/server/mobile/shoebox/types/v1/SaveShoeboxItem">
             <get:participantId>1191152220010</get:participantId>
             <get:participantCode>131029</get:participantCode>
             <get:groupCode>027198</get:groupCode>
             <get:userType>EE</get:userType>
             <get:clientName>Test</get:clientName>
             <get:shoeboxID>123444</get:shoeboxID>
             <get:imageID>45235</get:imageID>
          </get:SaveShoeBoxitemRequest>
       </soapenv:Body>
    </soapenv:Envelope>

嗨Dimitre,感谢您的回复。我需要XSLT是动态的,就像如果在“Payload”标记下出现任何oot relement,它应相应地构造soap信封。就像我们只为“GetImageRequest”构建一样。我们如何在XSLT中实现这一点。需要您的帮助来完成此操作。 - user5458829
@user5458829,如果您指定如何从<payload>的相应子元素构造顶级生成元素(例如“<GetShoeboxItemRequest>”)的名称规则,以及如何从源XML文档中的相应元素构造命名空间字符串值,则这是可能的。如果没有稳定的约定,这些都可以作为全局参数提供。 - Dimitre Novatchev
嗨,Dimitre,感谢您的评论。像我一样,在“负载”下有5个可能的传入请求:
  1. GetImageRequest
  2. SaveShoeBoxitemRequest
  3. SaveClaimWithReceiptRequest
  4. GetThumbNailImageRequest和
  5. AttachReceiptwithExistingClaimRequest, 它们都属于“负载”元素,但在传入这些XML时,它们没有为这5个元素发送名称空间。因此,如果我知道这5个元素的所有名称空间,我可以将其放入一个XML文件中,并可以从该XML文件中检索。或者,像您所提到的那样,可以在全局文档中定义这些名称空间。XSLT代码片段将很有帮助。
- user5458829
只是更新一下,如果我有一个名为“GetImageRequest”的元素出现在“payload”下面,那么我们需要在SOAP消息的BODY下构建相同的元素。 - user5458829
@user5458829,我马上就要开始工作了——10-12小时后我会研究这个问题。是的,看起来可以做到,我会在我的更新答案中展示一种方法。 - Dimitre Novatchev
显示剩余9条评论

0
如果我知道这 5 个元素的所有名称空间,那么我就可以把它们放在一个 XML 文件中,并从该 XML 文件中检索出来。或者如你所提到的,在全局文档中定义这些名称空间。
如果您有一个使用哪个命名空间与哪个“根”元素名称的映射表,那么您同样可以将其保存在样式表本身中。
下面是一个示例(使用虚构的命名空间,因为您没有指定自己的): XSLT 1.0
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="http://www.example.com/my"
exclude-result-prefixes="my">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<my:ns-map>
    <ns root="GetImageRequest">urn:webservice/a/b/GetShoeboxItem</ns>
    <ns root="SaveShoeBoxitemRequest">urn:webservice/c/d/SaveShoeBoxitemRequest</ns>
    <ns root="SaveClaimWithReceiptRequest">urn:webservice/e/f/SaveClaimWithReceiptRequest</ns>
    <ns root="GetThumbNailImageRequest">urn:webservice/g/h/GetThumbNailImageRequest</ns>
    <ns root="AttachReceiptwithExistingClaimRequest">urn:webservice/i/k/AttachReceiptwithExistingClaimRequest</ns>
</my:ns-map>

<xsl:variable name="root" select="name(/request/payload/*)"/>
<xsl:variable name="ns" select="document('')/xsl:stylesheet/my:ns-map/ns[@root=$root]"/>

<xsl:template match="/">
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
        <soapenv:Header/>
        <soapenv:Body>
            <xsl:apply-templates select="request/payload/*" />
        </soapenv:Body>
    </soapenv:Envelope>
</xsl:template>     

<xsl:template match="*" >
    <xsl:element name="get:{local-name()}" namespace="{$ns}">
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates />
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接