2

我有一个基于 Excel 在另存为“XML Spreadsheet 2003 (*.xml)”时生成的 XML 文档。

电子表格本身包含一个带有标签层次结构的标题部分:

| ABCDEFGHI
-+------------------------------------------------ -----
1| a1 a2
2| a11 a12 a13 a21 a22
3| a111 a112 a121 a122 a131 a132 a221 a222

此层次结构存在于工作簿中的所有工作表上,并且在任何地方看起来或多或少都相同。

Excel XML 的工作方式与普通的 HTML 表格完全一样。( <row>s 包含<cell>s)。我已经能够将所有内容转换为这样的树形结构:

<node title="a1" col="1">
  <node title="a11" col="1">
    <node title="a111" col="1"/>
    <node title="a112" col="2"/>
  </node>
  <node title="a12" col="3">
    <node title="a121" col="3" />
    <node title="a122" col="4" />
  </node>
  <!-- and so on -->
</node>

但这里是复杂的:

  • 有多个工作表,因此每个工作表都有一棵树
  • 每个工作表上的层次结构可能略有不同,树将不相等(例如,工作表 2 可能有“a113”,而其他工作表没有)
  • 树的深度没有明确限制
  • 然而,标签在所有工作表中都是相同的,这意味着它们可以用于分组

我想将这些单独的树合并成一个看起来像这样的树:

<node title="a1">
  <col on="sheet1">1</col>
  <col on="sheet2">1</col>
  <node title="a11">
    <col on="sheet1">1</col>
    <col on="sheet2">1</col>
    <node title="a111">
      <col on="sheet1">1</col>
      <col on="sheet2">1</col>
    </node>
    <node title="a112">
      <col on="sheet1">2</col>
      <col on="sheet2">2</col>
    </node>
    <node title="a113"><!-- different here -->
      <col on="sheet2">3</col>
    </node>
  </node>
  <node title="a12">
    <col on="sheet1">3</col>
    <col on="sheet2">4</col>
    <node title="a121">
      <col on="sheet1">3</col>
      <col on="sheet2">4</col>
    </node>
    <node title="a122">
      <col on="sheet1">4</col>
      <col on="sheet2">5</col>
    </node>
  </node>
  <!-- and so on -->
</node>

理想情况下,我希望能够在我什至从 Excel XML 构建三个结构之前进行合并(如果你让我开始这样做,那就太好了)。但是由于我不知道我将如何做到这一点,所以在构建树之后进行合并(即:上述情况)就可以了。

谢谢你的时间。:)

4

2 回答 2

2

这是 XSLT 1.0 中一种可能的解决方案

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

    <xsl:template match="/*">
      <t>
        <xsl:apply-templates
           select="node[@title='a1'][1]">
          <xsl:with-param name="pOther"
            select="node[@title='a1'][2]"/>
        </xsl:apply-templates>
      </t>
    </xsl:template>

    <xsl:template match="node">
      <xsl:param name="pOther"/>

      <node title="{@title}">
        <col on="sheet1">
          <xsl:value-of select="@col"/>
        </col>
          <xsl:choose>
            <xsl:when test="not($pOther)">
              <xsl:apply-templates mode="copy">
                <xsl:with-param name="pSheet" select="'sheet1'"/>
              </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
              <col on="sheet2">
                <xsl:value-of select="$pOther/@col"/>
              </col>
              <xsl:for-each select=
                "node[@title = $pOther/node/@title]">

                <xsl:apply-templates select=".">
                  <xsl:with-param name="pOther" select=
                   "$pOther/node[@title = current()/@title]"/>
                </xsl:apply-templates>
              </xsl:for-each>

              <xsl:apply-templates mode="copy" select=
                "node[not(@title = $pOther/node/@title)]">
                <xsl:with-param name="pSheet" select="'sheet1'"/>
              </xsl:apply-templates>

              <xsl:apply-templates mode="copy" select=
                "$pOther/node[not(@title = current()/node/@title)]">
                <xsl:with-param name="pSheet" select="'sheet2'"/>
              </xsl:apply-templates>
            </xsl:otherwise>
          </xsl:choose>
      </node>
    </xsl:template>

    <xsl:template match="node" mode="copy">
      <xsl:param name="pSheet"/>

      <node title="{@title}">
        <col on="{$pSheet}">
          <xsl:value-of select="@col"/>
        </col>

        <xsl:apply-templates select="node" mode="copy">
          <xsl:with-param name="pSheet" select="$pSheet"/>
        </xsl:apply-templates>
      </node>
    </xsl:template>
</xsl:stylesheet>

当对这个 XML 文档应用上述转换时(两个 XML 文档在一个公共顶部节点下的连接——留给读者作为练习:)):

<t>
    <node title="a1" col="1">
        <node title="a11" col="1">
            <node title="a111" col="1"/>
            <node title="a112" col="2"/>
        </node>
        <node title="a12" col="3">
            <node title="a121" col="3" />
            <node title="a122" col="4" />
        </node>
        <!-- and so on -->
    </node>
    <node title="a1" col="1">
        <node title="a11" col="1">
            <node title="a111" col="1"/>
            <node title="a112" col="2"/>
            <node title="a113" col="3"/>
        </node>
        <node title="a12" col="4">
            <node title="a121" col="4" />
            <node title="a122" col="5" />
        </node>
        <!-- and so on -->
    </node>
</t>

产生了想要的结果:

<t>
    <node title="a1">
        <col on="sheet1">1</col>
        <col on="sheet2">1</col>
        <node title="a11">
            <col on="sheet1">1</col>
            <col on="sheet2">1</col>
            <node title="a111">
                <col on="sheet1">1</col>
                <col on="sheet2">1</col>
            </node>
            <node title="a112">
                <col on="sheet1">2</col>
                <col on="sheet2">2</col>
            </node>
            <node title="a113">
                <col on="sheet2">3</col>
            </node>
        </node>
        <node title="a12">
            <col on="sheet1">3</col>
            <col on="sheet2">4</col>
            <node title="a121">
                <col on="sheet1">3</col>
                <col on="sheet2">4</col>
            </node>
            <node title="a122">
                <col on="sheet1">4</col>
                <col on="sheet2">5</col>
            </node>
        </node>
    </node>
</t>

请注意以下事项:

  1. 我们假设两个顶级node元素都具有"a1"title属性的值。这很容易概括。

  2. 模板匹配node有一个名为 的参数,它是从另一个文档中pOther命名的相应元素。node仅当 $pOther存在时才应用此模板。

  3. 当不存在名为的对应元素node时,将应用另一个模板,也匹配node,但处于模式copy中。该模板有一个名为 的参数pSheet,其值为该元素所属的工作表名称(字符串)。

于 2009-04-01T04:20:28.163 回答
1

一个以工作表编号作为参数的可调用模板如何检查输入并返回正确的“col”节点,如果它出现在该工作表的 XML 中,如果没有,则返回任何内容。在每个节点上,为每个工作表调用一次。

为了合并树,可能是一个模板,它在任何工作表中查找当前节点的所有子节点,并为每个子节点递归。

抱歉没有示例代码,我发现编写 XSLT 很慢,可能是因为我不经常这样做。所以我很可能错过了一些重要的事情。但是把它们放在一起会得到类似的东西:

  • 获取“/node”的标题。有了这个标题:
    • 在所有工作表中搜索此标题,为每个工作表发出“col”节点
    • 在所有工作表中搜索具有此标题的节点的子节点(丢弃重复项)
    • 递归每个标题。

以下是一些用于以各种方式删除重复项的片段:

http://www.dpawson.co.uk/xsl/sect2/N2696.html

读取多个文档是依赖于处理器的,但是如果所有其他方法都失败了,使用任何旧的脚本语言可能会做一些剪切和粘贴,前提是您知道它们都将具有相同的编码,不要使用冲突的 id,等等。

于 2009-03-31T18:21:08.617 回答