平面到分层 XML 转换 [英] Flat to hierarchical XML transformation

查看:24
本文介绍了平面到分层 XML 转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是源 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 列表格数据.数据按第 1 列排序并包含类型",其顺序为:actualaccumulationdeposit 但有些可以根本不存在(例如累积).即它实际上包含这些数据:

This list contains 6-column table data. Data is ordered by 1-st column and contain 'type', which will come in order: actual, accumulation, deposit but some can be absent at all (accumulation in example). i.e. it's actually contain this data:

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 输出(基于示例):

Here is desired XML output (based on example):

<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 列的值.

Where *_percentage tags should contain value of 5th column from any row from associated * set.

这是我到目前为止:

<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.

which do everything except whose *_percentage tags.. I'm limited to XSLT 1.0.

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

UPDATE WITH FINAL ANSWER: with small fixes to Maesto13 solution, and only works on 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>

推荐答案

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

I would prefer a two step transformation, collecting the data into a variable first. When vast data size is involved, this may have to be reconsidered. Below is an xslt which does the trick.

<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>

这篇关于平面到分层 XML 转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆