从平面到分层的XML转换

时间:2012-02-09 08:54:52

标签: xslt xslt-1.0

以下是源XML:

<CellData>
  <Cell CellOrdinal="0">
    <Value>actual</Value>
    <FmtValue>actual</FmtValue>
  </Cell>
  <Cell CellOrdinal="1">
    <Value>630961942</Value>
    <FmtValue>630961942</FmtValue>
  </Cell>
  <Cell CellOrdinal="2">
    <Value>2.045711422E7</Value>
    <FmtValue>20457114.2200</FmtValue>
  </Cell>
  <Cell CellOrdinal="3">
    <Value>9.997105219639378E1</Value>
    <FmtValue>99.9711</FmtValue>
  </Cell>
  <Cell CellOrdinal="4">
    <Value>3.33E1</Value>
    <FmtValue>33.0000</FmtValue>
  </Cell>
  <Cell CellOrdinal="5">
    <Value>2.046303782E7</Value>
    <FmtValue>20463037.8200</FmtValue>
  </Cell>
  <Cell CellOrdinal="6">
    <Value>deposit</Value>
    <FmtValue>deposit</FmtValue>
  </Cell>
  <Cell CellOrdinal="7">
    <Value>144783359</Value>
    <FmtValue>144783359</FmtValue>
  </Cell>
  <Cell CellOrdinal="8">
    <Value>2.1388E2</Value>
    <FmtValue>213.8800</FmtValue>
  </Cell>
  <Cell CellOrdinal="9">
    <Value>1.0452016063370595E-3</Value>
    <FmtValue>0.0010</FmtValue>
  </Cell>
  <Cell CellOrdinal="10">
    <Value>6.67E1</Value>
    <FmtValue>67.0000</FmtValue>
  </Cell>
  <Cell CellOrdinal="11">
    <Value>2.046303782E7</Value>
    <FmtValue>20463037.8200</FmtValue>
  </Cell>
  <Cell CellOrdinal="12">
    <Value>deposit</Value>
    <FmtValue>deposit</FmtValue>
  </Cell>
  <Cell CellOrdinal="13">
    <Value>304011203</Value>
    <FmtValue>304011203</FmtValue>
  </Cell>
  <Cell CellOrdinal="14">
    <Value>5.70972E3</Value>
    <FmtValue>5709.7200</FmtValue>
  </Cell>
  <Cell CellOrdinal="15">
    <Value>2.7902601999882342E-2</Value>
    <FmtValue>0.0279</FmtValue>
  </Cell>
  <Cell CellOrdinal="16">
    <Value>6.67E1</Value>
    <FmtValue>67.0000</FmtValue>
  </Cell>
  <Cell CellOrdinal="17">
    <Value>2.046303782E7</Value>
    <FmtValue>20463037.8200</FmtValue>
  </Cell>
</CellData>

此列表包含6列表数据。 数据按第一列排序并包含“类型”,它将按顺序排列:actualaccumulationdeposit但有些可能根本不存在(例如,累积)。 即它实际上包含这些数据:

contract_type   contract_id sum             percentage  contract_type_percentage    balance_total
actual          630961942   20457114.2200   99.9711     33.0000                     20463037.8200
deposit         144783359   213.8800        0.0010      67.0000                     20463037.8200
deposit         304011203   5709.7200       0.0279      67.0000                     20463037.8200

这是所需的XML输出(基于示例):

<body>
  <actual_accounts>
    <actual_account>
      <contract_id>630961942</contract_id>
      <sum>20457114.2200</sum>
      <percentage>99.9711</percentage>
    </actual_account>
    <actual_percentage>33.0000</actual_percentage>
  </actual_accounts>
  <accumulation_accounts>
    <accumulation_percentage>0</accumulation_percentage>
  </accumulation_accounts>
  <deposits>
    <deposit>
      <contract_id>144783359</contract_id>
      <sum>213.8800</sum>
      <percentage>0.0010</percentage>
    </deposit>
    <deposit>
      <contract_id>304011203</contract_id>
      <sum>5709.7200</sum>
      <percentage>0.0279</percentage>
    </deposit>
    <deposit_percentage>67.0000</deposit_percentage>
  </deposits>
  <balance_total>20463037.8200</balance_total>
</body>

其中*_percentage代码应包含来自关联*集合中任意行的第5列的值。

这是我到目前为止:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" omit-xml-declaration="yes" method="xml" version="1.0"/>
    <xsl:template match="/">
        <body>
            <actual_accounts>
                <xsl:apply-templates select="//Cell">
                    <xsl:with-param name="cellType" select="'actual_accounts'"/>
                </xsl:apply-templates>
                <actual_percentage>0</actual_percentage>
            </actual_accounts>
            <accumulation_accounts>
                <xsl:apply-templates select="//Cell">
                    <xsl:with-param name="cellType" select="'accumulation_accounts'"/>
                </xsl:apply-templates>
                <accumulation_percentage>0</accumulation_percentage>
            </accumulation_accounts>
            <deposits>
                <xsl:apply-templates select="//Cell">
                    <xsl:with-param name="cellType" select="'deposits'"/>
                </xsl:apply-templates>
                <deposit_percentage>0</deposit_percentage>
            </deposits>
            <xsl:choose>
                <xsl:when test="count(CellData/Cell) &gt;= 6">
                    <balance_total>
                        <xsl:value-of select="CellData/Cell[6]/FmtValue"/>
                    </balance_total>
                </xsl:when>
                <xsl:otherwise>
                    <balance_total>0</balance_total>
                </xsl:otherwise>
            </xsl:choose>
        </body>
    </xsl:template>

    <xsl:template match="//Cell">
        <xsl:param name="cellType"/>

        <xsl:if test="@CellOrdinal mod 6 = 0">
            <xsl:variable name="contract_type" select="FmtValue"/>
            <xsl:variable name="contract_id" select="@CellOrdinal + 2"/>
            <xsl:variable name="sum" select="@CellOrdinal + 3"/>
            <xsl:variable name="percentage" select="@CellOrdinal + 4"/>
            <xsl:variable name="type_percentage" select="@CellOrdinal + 5"/>
            <xsl:variable name="balance_total" select="@CellOrdinal + 6"/>

            <xsl:if test="$contract_type = 'actual' and $cellType = ('actual_accounts')">
                <actual_account>
                    <contract_id>
                        <xsl:value-of select="parent::CellData/Cell[$contract_id]/FmtValue"/>
                    </contract_id>
                    <sum>
                        <xsl:value-of select="parent::CellData/Cell[$sum]/FmtValue"/>
                    </sum>
                    <percentage>
                        <xsl:value-of select="parent::CellData/Cell[$percentage]/FmtValue"/>
                    </percentage>
                </actual_account>
            </xsl:if>

            <xsl:if test="$contract_type = 'accumulation' and $cellType = ('accumulation_accounts')">
                <accumulation_account>
                    <contract_id>
                        <xsl:value-of select="parent::CellData/Cell[$contract_id]/FmtValue"/>
                    </contract_id>
                    <sum>
                        <xsl:value-of select="parent::CellData/Cell[$sum]/FmtValue"/>
                    </sum>
                    <percentage>
                        <xsl:value-of select="parent::CellData/Cell[$percentage]/FmtValue"/>
                    </percentage>
                </accumulation_account>
            </xsl:if>

            <xsl:if test="$contract_type = 'deposit' and $cellType = 'deposits'">
                <deposit>
                    <contract_id>
                        <xsl:value-of select="parent::CellData/Cell[$contract_id]/FmtValue"/>
                    </contract_id>
                    <sum>
                        <xsl:value-of select="parent::CellData/Cell[$sum]/FmtValue"/>
                    </sum>
                    <percentage>
                        <xsl:value-of select="parent::CellData/Cell[$percentage]/FmtValue"/>
                    </percentage>
                </deposit>
            </xsl:if>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

除了*_percentage个标签以外的所有内容 我只限于XSLT 1.0。

更新最终答案:对Maesto13解决方案进行小修复,仅适用于MSXML4.0 +,.NET1.0 +

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxml="urn:schemas-microsoft-com:xslt" extension-element-prefixes="msxml">

    <xsl:output indent="yes" omit-xml-declaration="yes" method="xml" version="1.0"/>

    <xsl:key name="CellGroup" use="@CellOrdinal - (@CellOrdinal mod 6)" match="CellData/Cell"/>

    <xsl:template match="CellData">
        <xsl:variable name="Cells">
            <xsl:apply-templates select="Cell[generate-id() = generate-id(key('CellGroup', @CellOrdinal - (@CellOrdinal mod 6))[1])]" mode="group"/>
        </xsl:variable>
        <body>
            <actual_accounts>
                <xsl:for-each select="msxml:node-set($Cells)/Cell[contract-type='actual']">
                    <actual_account>
                        <xsl:copy-of select="contract-type"/>
                        <xsl:copy-of select="sum"/>
                        <xsl:copy-of select="percentage"/>
                    </actual_account>
                </xsl:for-each>
                <xsl:variable name="type_percentage" select="msxml:node-set($Cells)/Cell[contract-type='actual'][1]/contract-type-percentage[1]"></xsl:variable>
                <xsl:choose>
                    <xsl:when test="boolean($type_percentage)">
                        <actual_percentage><xsl:value-of select="$type_percentage"/></actual_percentage>
                    </xsl:when>
                    <xsl:otherwise>
                        <actual_percentage>0</actual_percentage>
                    </xsl:otherwise>
                </xsl:choose>
            </actual_accounts>
            <accumulation_accounts>
                <xsl:for-each select="msxml:node-set($Cells)/Cell[contract-type='accumulation']">
                    <accumulation_account>
                        <xsl:copy-of select="contract-type"/>
                        <xsl:copy-of select="sum"/>
                        <xsl:copy-of select="percentage"/>
                    </accumulation_account>
                </xsl:for-each>
                <xsl:variable name="type_percentage" select="msxml:node-set($Cells)/Cell[contract-type='accumulation'][1]/contract-type-percentage[1]"></xsl:variable>
                <xsl:choose>
                    <xsl:when test="boolean($type_percentage)">
                        <accumulation_percentage><xsl:value-of select="$type_percentage"/></accumulation_percentage>
                    </xsl:when>
                    <xsl:otherwise>
                        <accumulation_percentage>0</accumulation_percentage>
                    </xsl:otherwise>
                </xsl:choose>
            </accumulation_accounts>
            <deposits>
                <xsl:for-each select="msxml:node-set($Cells)/Cell[contract-type='deposit']">
                    <deposit>
                        <xsl:copy-of select="contract-type"/>
                        <xsl:copy-of select="sum"/>
                        <xsl:copy-of select="percentage"/>
                    </deposit>
                </xsl:for-each>
                <xsl:variable name="type_percentage" select="msxml:node-set($Cells)/Cell[contract-type='deposit'][1]/contract-type-percentage[1]"></xsl:variable>
                <xsl:choose>
                    <xsl:when test="boolean($type_percentage)">
                        <deposit_percentage><xsl:value-of select="$type_percentage"/></deposit_percentage>
                    </xsl:when>
                    <xsl:otherwise>
                        <deposit_percentage>0</deposit_percentage>
                    </xsl:otherwise>
                </xsl:choose>
            </deposits>
            <xsl:variable name="total" select="msxml:node-set($Cells)/Cell[1]/balance-total"></xsl:variable>
            <xsl:choose>
                <xsl:when test="boolean($total)">
                    <balance_total>
                        <xsl:value-of select="$total"/>
                    </balance_total>
                </xsl:when>
                <xsl:otherwise><balance_total>0</balance_total></xsl:otherwise>
            </xsl:choose>
        </body>
    </xsl:template>

    <xsl:template match="Cell" mode="group">
        <Cell>
            <xsl:variable name="Cells" select="key('CellGroup', @CellOrdinal - (@CellOrdinal mod 6))"/>
            <contract-type><xsl:value-of select="$Cells[1]/FmtValue"/></contract-type>
            <contract-id><xsl:value-of select="$Cells[2]/FmtValue"/></contract-id>
            <sum><xsl:value-of select="$Cells[3]/FmtValue"/></sum>
            <percentage><xsl:value-of select="$Cells[4]/FmtValue"/></percentage>
            <contract-type-percentage><xsl:value-of select="$Cells[5]/FmtValue"/></contract-type-percentage>
            <balance-total><xsl:value-of select="$Cells[6]/FmtValue"/></balance-total>
        </Cell>
    </xsl:template>

</xsl:stylesheet>

1 个答案:

答案 0 :(得分:1)

我更喜欢两步转换,首先将数据收集到变量中。当涉及大量数据时,可能必须重新考虑这一点。下面是一个xslt可以解决问题。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes" omit-xml-declaration="yes" method="xml" version="1.0"/>

    <xsl:key name="CellGroup" use="@CellOrdinal - (@CellOrdinal mod 6)" match="CellData/Cell"/>

    <xsl:template match="CellData">
        <xsl:variable name="Cells">
            <xsl:apply-templates select="Cell[generate-id() = generate-id(key('CellGroup', @CellOrdinal - (@CellOrdinal mod 6))[1])]" mode="group"/>
        </xsl:variable>
        <body>
            <actual_accounts>
                <xsl:for-each select="$Cells/Cell[contract-type='actual']">
                    <actual_account>
                        <xsl:copy-of select="contract-type"/>
                        <xsl:copy-of select="sum"/>
                        <xsl:copy-of select="percentage"/>
                    </actual_account>
                </xsl:for-each>
                <actual_percentage><xsl:value-of select="$Cells/Cell[contract-type='actual'][1]/contract-type-percentage"/></actual_percentage>
            </actual_accounts>
            <accumulation_accounts>
                <xsl:for-each select="$Cells/Cell[contract-type='accumulation']">
                    <accumulation_account>
                        <xsl:copy-of select="contract-type"/>
                        <xsl:copy-of select="sum"/>
                        <xsl:copy-of select="percentage"/>
                    </accumulation_account>
                </xsl:for-each>
                <accumulation_percentage><xsl:value-of select="$Cells/Cell[contract-type='accumulation'][1]/contract-type-percentage[1]"/></accumulation_percentage>
            </accumulation_accounts>
            <deposits>
                <xsl:for-each select="$Cells/Cell[contract-type='deposit']">
                    <deposit>
                        <xsl:copy-of select="contract-type"/>
                        <xsl:copy-of select="sum"/>
                        <xsl:copy-of select="percentage"/>
                    </deposit>
                </xsl:for-each>
                <deposit_percentage><xsl:value-of select="$Cells/Cell[contract-type='deposit'][1]/contract-type-percentage[1]"/></deposit_percentage>
            </deposits>
            <balance_total><xsl:value-of select="$Cells/Cell[1]/balance-total"/></balance_total>
        </body>
    </xsl:template>

    <xsl:template match="Cell" mode="group">
        <Cell>
            <xsl:variable name="Cells" select="key('CellGroup', @CellOrdinal - (@CellOrdinal mod 6))"/>
            <contract-type><xsl:value-of select="$Cells[1]/FmtValue"/></contract-type>
            <contract-id><xsl:value-of select="$Cells[2]/FmtValue"/></contract-id>
            <sum><xsl:value-of select="$Cells[3]/FmtValue"/></sum>
            <percentage><xsl:value-of select="$Cells[4]/FmtValue"/></percentage>
            <contract-type-percentage><xsl:value-of select="$Cells[5]/FmtValue"/></contract-type-percentage>
            <balance-total><xsl:value-of select="$Cells[6]/FmtValue"/></balance-total>
        </Cell>
    </xsl:template>

</xsl:stylesheet>

我得到的输出如下:

<body>
    <actual_accounts>
        <actual_account>
            <contract-type>actual</contract-type>
            <sum>20457114.2200</sum>
            <percentage>99.9711</percentage>
        </actual_account>
        <actual_percentage>33.0000</actual_percentage>
    </actual_accounts>
    <accumulation_accounts>
        <accumulation_percentage></accumulation_percentage>
    </accumulation_accounts>
    <deposits>
        <deposit>
            <contract-type>deposit</contract-type>
            <sum>213.8800</sum>
            <percentage>0.0010</percentage>
        </deposit>
        <deposit>
            <contract-type>deposit</contract-type>
            <sum>5709.7200</sum>
            <percentage>0.0279</percentage>
        </deposit>
        <deposit_percentage>67.0000</deposit_percentage>
    </deposits>
    <balance_total>20463037.8200</balance_total>
</body>