【问题标题】:Creating XSLT transform to flatten multiRef encoded SOAP message创建 XSLT 转换以展平 multiRef 编码的 SOAP 消息
【发布时间】:2011-03-03 19:17:40
【问题描述】:

输入是一个 mutliRef 编码的 SOAP 消息/文档。你如何使用 XSLT 扁平化 multiRefs。 Multiref节点可以被引用使用 多次,并且它们自己递归地引用其他 multiRef 节点。

结构中唯一可以安全引用的部分是 multiRef 元素和@id 和@href 属性。其他元素或 命名空间可能会改变。

示例输入消息是:

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <ns1:getAccountDTOResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:ns1="http://www.example.com/pw/services/PWServices">
      <getAccountDTOReturn href="#id0" />
    </ns1:getAccountDTOResponse>
    <multiRef id="id0" soapenc:root="0"
    soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xsi:type="ns2:Account"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:ns2="urn:PWServices">
      <ID href="#id1" />
      <accountNumber xsi:type="soapenc:string"></accountNumber>
      <accountType xsi:type="soapenc:string"></accountType>
      <clientData xsi:type="soapenc:Array" xsi:nil="true" />
      <name xsi:type="soapenc:string"></name>
      <parentRef xsi:type="soapenc:string"></parentRef>
    </multiRef>
    <multiRef id="id1" soapenc:root="0"
    soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
    xsi:type="xsd:long"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
    0</multiRef>
  </soapenv:Body>
</soapenv:Envelope>

预期输出是:

<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <soapenv:Body>
    <ns1:getAccountDTOResponse xmlns:ns1="http://www.example.com/pw/services/PWServices"
    soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">

      <getAccountDTOReturn xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:ns2="urn:PWServices"
      soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
      xsi:type="ns2:Account">
        <ns1:ID soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
        xsi:type="xsd:long">0</ns1:ID>
        <ns1:accountNumber xsi:type="soapenc:string" />
        <ns1:accountType xsi:type="soapenc:string" />
        <ns1:clientData xsi:type="soapenc:Array" xsi:nil="true" />
        <ns1:name xsi:type="soapenc:string" />
        <ns1:parentRef xsi:type="soapenc:string" />
      </getAccountDTOReturn>
    </ns1:getAccountDTOResponse>
  </soapenv:Body>
</soapenv:Envelope>

更新: 在上面的例子中,从逻辑上讲,应该发生的事情是:

在第一次通过时,getAccountDTOResponse 包含@href="#id0",因此该元素将替换为所有具有@id="id0" 的子 multiRef 元素,但除外。

在第二遍中,@href="#id1" 应该被发现,并且 ID 元素应该被替换为 @id="id1" 元素的内容。

输出中不应存在 multiRef 元素。如果涉及到整个 multiRef 混乱,则输出中不应存在 @id 或 @href 属性。

【问题讨论】:

  • 目前还不是很清楚到底应该放入什么。能否请您将此添加到您的问题中?
  • @Dimitre 我在问题的底部添加了一些说明。

标签: xml xslt soap


【解决方案1】:

Alex,我没有完全匹配您的输出,但这里是您可以通过 href 解析文档的方法。

这个样式表:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" >

    <xsl:key name="multiref-by-id" match="multiRef" use="@id"/>

    <xsl:template match="/">
        <xsl:copy>
            <xsl:apply-templates select="@*|*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[starts-with(@href, '#')]">
        <xsl:copy>
            <xsl:apply-templates select="@* |
             key('multiref-by-id', substring-after(@href, '#'))/@* |
            key('multiref-by-id', substring-after(@href, '#'))/node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="@href[starts-with(., '#')] | multiRef[@id] | @soapenc:root"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

鉴于此输入:

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <ns1:getAccountDTOResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                                   xmlns:ns1="http://www.example.com/pw/services/PWServices">
            <getAccountDTOReturn href="#id0"/>
        </ns1:getAccountDTOResponse>
        <multiRef id="id0" soapenc:root="0"
                  soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                  xsi:type="ns2:Account"
                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                  xmlns:ns2="urn:PWServices">
            <ID href="#id1"/>
            <accountNumber xsi:type="soapenc:string"></accountNumber>
            <accountType xsi:type="soapenc:string"></accountType>
            <clientData xsi:type="soapenc:Array" xsi:nil="true"/>
            <name xsi:type="soapenc:string"></name>
            <parentRef xsi:type="soapenc:string"></parentRef>
        </multiRef>
        <multiRef id="id1" soapenc:root="0"
                  soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                  xsi:type="xsd:long"
                  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
            0
        </multiRef>
    </soapenv:Body>
</soapenv:Envelope>

产生以下结果:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <ns1:getAccountDTOResponse xmlns:ns1="http://www.example.com/pw/services/PWServices"
                                   soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
            <getAccountDTOReturn id="id0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                                 xsi:type="ns2:Account">
                <ID xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices" id="id1"
                    soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="xsd:long">
                    0
                </ID>
                <accountNumber xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
                               xsi:type="soapenc:string"/>
                <accountType xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
                             xsi:type="soapenc:string"/>
                <clientData xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
                            xsi:type="soapenc:Array" xsi:nil="true"/>
                <name xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
                      xsi:type="soapenc:string"/>
                <parentRef xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns2="urn:PWServices"
                           xsi:type="soapenc:string"/>
            </getAccountDTOReturn>
        </ns1:getAccountDTOResponse>


    </soapenv:Body>
</soapenv:Envelope>

我认为这个 apporach 可以很容易地定制以满足您的需求。我想概述一下提供的样式表在@hrefs 上运行,并且不考虑任何元素名称。因此可以灵活使用,无需注意引用元素名称。然而,所有的引用都应该命名为multiRefs,但这也可以很容易地定制。

【讨论】:

  • 认为这是对的。认为其中一个区别是没有从输出中删除 @id 属性,另一个是我的示例输出引用了 ns1 命名空间,不确定来自哪里。这可能是该函数的命令式版本中的一个错误。
【解决方案2】:

这个样式表:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
    <xsl:key name="kMultiRefById" match="multiRef" use="@id"/>
    <xsl:output method="xml"/>
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="getAccountDTOReturn">
        <xsl:variable name="vRefer"
                      select="key('kMultiRefById',substring(@href,2))"/>
        <xsl:copy>
            <xsl:copy-of select="$vRefer/namespace::*"/>
            <xsl:apply-templates select="$vRefer/@*|$vRefer/node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="multiRef|multiRef/@id|multiRef/@soapenc:root"/>
</xsl:stylesheet>

输出:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <ns1:getAccountDTOResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                                   xmlns:ns1="http://www.example.com/pw/services/PWServices">
            <getAccountDTOReturn soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
                                 xsi:type="ns2:Account"
                                 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                                 xmlns:ns2="urn:PWServices">
                <ID href="#id1"></ID>
                <accountNumber xsi:type="soapenc:string"></accountNumber>
                <accountType xsi:type="soapenc:string"></accountType>
                <clientData xsi:type="soapenc:Array" xsi:nil="true"></clientData>
                <name xsi:type="soapenc:string"></name>
                <parentRef xsi:type="soapenc:string"></parentRef>
            </getAccountDTOReturn>
        </ns1:getAccountDTOResponse>
    </soapenv:Body>
</soapenv:Envelope>

注意:交叉引用的键。身份规则。剥离的空规则。 可能并非每个 XSLT 处理器都可以复制命名空间节点,尽管我只知道 Mozilla 的 TransforMiiX 尚未实现 namespaces:: 轴。

【讨论】:

  • 这很接近,但还不够完整。在 ID 元素中,未解析 href="#id1"。另外,不能依赖 getAccountDTOReturn,因为它会改变,我认为匹配必须基于 @href 存在。
猜你喜欢
  • 1970-01-01
  • 2011-07-07
  • 1970-01-01
  • 2021-07-31
  • 2017-07-11
  • 2014-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多