平面到分层XSLT转换

时间:2014-12-29 21:15:32

标签: xml xslt xslt-1.0 hierarchical flat

我很难找到如何使用XSLT V1转换将平面XML转换为分层结果。我会发布我的XSLT,但我不确定我是否正确接近它。我知道我需要创建和使用Keys,但是,无论是否尝试应用我在这里和其他地方找到的每个例子(很多很多很多......),它根本不起作用。

这是输入:

<env:Envelope xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
    <env:Header/>
    <env:Body>
        <tns:getClientRS xmlns:tns="http://services.xxx.com/ClientService">
            <tns:Acknowledgement>Process completed successfully.</tns:Acknowledgement>
            <tns:client>
                <tns:ClientID>515164</tns:ClientID>
                <tns:episodeID>1</tns:episodeID>
                <tns:guarantorID>1</tns:guarantorID>
            </tns:client>
            <tns:client>
                <tns:ClientID>515164</tns:ClientID>
                <tns:episodeID>1</tns:episodeID>
                <tns:guarantorID>2</tns:guarantorID>
            </tns:client>
            <tns:plan>
                <tns:ClientID>515164</tns:ClientID>
                <tns:episodeID>1</tns:episodeID>
                <tns:guarantorID>1</tns:guarantorID>
                <tns:guarantorPlan>1</tns:guarantorPlan>
            </tns:plan>
            <tns:plan>
                <tns:ClientID>515164</tns:ClientID>
                <tns:episodeID>1</tns:episodeID>
                <tns:guarantorID>2</tns:guarantorID>
                <tns:guarantorPlan>2</tns:guarantorPlan>
            </tns:plan>
        </tns:getClientRS>
    </env:Body>
</env:Envelope>

和所需的响应XML:

<env:Envelope xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
    <env:Header/>
    <env:Body>
        <tns:getClientRS xmlns:tns="http://services.xxx.com/ClientService">
            <tns:Acknowledgement>Process completed successfully.</tns:Acknowledgement>
            <tns:clients>
                <tns:ClientID>515164</tns:ClientID>
                <tns:episodes>
                    <tns:Episode>
                        <tns:episodeID>1</tns:episodeID>
                        <tns:Guarantors>
                            <tns:Guarantor>
                                <tns:guarantorID>1</tns:guarantorID>
                                <tns:Plan>
                                    <tns:guarantorPlan>1</tns:guarantorPlan>
                                </tns:Plan>
                            </tns:Guarantor>
                            <tns:Guarantor>
                                <tns:guarantorID>2</tns:guarantorID>
                                <tns:Plan>
                                    <tns:guarantorPlan>2</tns:guarantorPlan>
                                </tns:Plan>
                            </tns:Guarantor>
                        </tns:Guarantors>
                    </tns:Episode>
                </tns:episodes>
            </tns:clients>
        </tns:getClientRS>
    </env:Body>
</env:Envelope>

只是想让事情变得简单,但可能会有多个客户端和多个剧集。我怀疑,如果我能找出保证人,我可以找出剧集和客户。

我真的需要帮助。无论我做什么,我只 每个键的第一个......即担保人1和计划1.就是这样。

2 个答案:

答案 0 :(得分:1)

我建议你这样试试:

XSLT 1.0 + EXSLT设置:distinct()

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:tns="http://services.xxx.com/ClientService"
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="set">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="client" match="tns:client" use="tns:ClientID" />
<xsl:key name="plan" match="tns:plan" use="concat(tns:ClientID, '|', tns:episodeID)" />

<xsl:template match="/">
    <env:Envelope xmlns:wsa="http://www.w3.org/2005/08/addressing">
        <env:Header/>
        <env:Body>
            <tns:getClientRS>
                <xsl:copy-of select="tns:Acknowledgement"/>
                <tns:clients>
                    <!-- client -->
                    <xsl:for-each select="set:distinct(env:Envelope/env:Body/tns:getClientRS/tns:client/tns:ClientID)">
                        <xsl:variable name="clientID" select="." />
                        <xsl:copy-of select="."/>
                         <tns:episodes>
                            <!-- episode -->
                            <xsl:for-each select="set:distinct(key('client', .)/tns:episodeID)">
                                <tns:Episode>
                                    <xsl:variable name="episodeID" select="." />
                                    <xsl:copy-of select="."/>
                                        <!-- plans -->
                                        <tns:Guarantors>
                                            <xsl:for-each select="key('plan', concat($clientID, '|', $episodeID))">
                                                <tns:Guarantor>
                                                    <xsl:copy-of select="tns:guarantorID"/>
                                                    <tns:Plan>
                                                        <xsl:copy-of select="tns:guarantorPlan"/>
                                                    </tns:Plan>
                                                </tns:Guarantor>
                                            </xsl:for-each> 
                                        </tns:Guarantors>
                                </tns:Episode>
                            </xsl:for-each> 
                        </tns:episodes> 
                    </xsl:for-each> 
                </tns:clients>
            </tns:getClientRS>
        </env:Body>
    </env:Envelope>
</xsl:template>

</xsl:stylesheet>

这将返回输入示例的预期结果 - 尽管这可能部分是巧合。如果每个担保人有多个计划,则需要再增加一个级别。

答案 1 :(得分:0)

不打算写一个完整的XSLT文件,但是你尝试过以下方法吗?

<xsl:foreach select="tns:client">
  <xsl:if test="
    not( preceding-sibling::tns:getClientRS[ 
      not(./tns:ClientID = current()/tns:ClientID ) 
    ] )
  ">
  <xsl:variable name="ClientID" select="tns:ClientID"/>

然后根据需要遍历客户端和计划(即select="ancestor::tns:getClientRS/tns:plan[tns:ClientId = $ClientID]")。