【问题标题】:XSLT flat structure to hiearchicalXSLT 平面结构转分层
【发布时间】:2015-02-09 18:42:09
【问题描述】:

我有一个现有的 xml 文件,其中包含以下格式的数据。代码的前三个字母确定同一组。

    <Subjects>
        <subject>
        <code>ANT001000</code>
            <literal>ANTIQUES / Americana</literal>
        </subject>
        <subject>
            <code>ANT002000</code>
            <literal>ANTIQUES / Art</literal>
        </subject>
        <subject>
            <code>CKB100000</code>
            <literal>COOKING / Beverages / General</literal>
        </subject>
        <subject>
            <code>CKB006000</code>
            <literal>COOKING / Beverages / Bartending</literal>
        </subject>
    </Subjects>

我需要把它改成这样:

    <node name="Antiques" id="1">
        <node name="Americana" id="2" />
        <node name="Art" id="3" />
    </node>
<node name="Cooking" id="4">
        <node name="Beverages " id="6" />
            <node name="General" id="7" />
            <node name="Bartending" id="8" />
        </node>
</node>

我尝试了几种方法,但都无法奏效。任何想法将不胜感激。

谢谢

【问题讨论】:

  • 请至少展示其中一种“几种方法”。谢谢!
  • 这并不简单。请指明是使用 XSLT 1.0 还是 2.0。 -- 剩下的代码有什么意义吗(也许可以用来使这更容易)?

标签: xml xslt hierarchy


【解决方案1】:

我建议分几次这样做:

第一遍将对每个subjectliteral 元素中列出的类别进行标记,并为每个类别创建一个category 节点。在给定的示例中,这将导致:

  <category path="">ANTIQUES</category>
  <category path="ANTIQUES/">Americana</category>
  <category path="">ANTIQUES</category>
  <category path="ANTIQUES/">Art</category>
  <category path="">COOKING</category>
  <category path="COOKING/">Beverages</category>
  <category path="COOKING/Beverages/">General</category>
  <category path="">COOKING</category>
  <category path="COOKING/">Beverages</category>
  <category path="COOKING/Beverages/">Bartending</category>

下一步将选择所有顶级类别(即具有空 @path 属性的类别):

  <category path="">ANTIQUES</category>
  <category path="">ANTIQUES</category>
  <category path="">COOKING</category>
  <category path="">COOKING</category>

并进一步减少它以仅包含不同的值:

  <category path="">ANTIQUES</category>
  <category path="">COOKING</category>

现在我们终于有了一个不错的起点,我们可以将模板应用到每个这样的类别来做:

<xsl:template match="category">
    <node name="{.}" id="{generate-id()}">
        <xsl:apply-templates select="$child-categories"/>
    </node>
</xsl:template>

其中$child-categories 表示选择其@path 属性与当前@path 和当前值的串联匹配的类别的表达式。

我使用每个类别的完整路径,以防止在类别名称跨分支不唯一的情况下出现误报匹配。


作为概念验证,我编写了以下样式表,它利用了一些 EXSLT 扩展函数,即:exsl:node-set()、str:tokenize() 和 set:distinct():

XSLT 1.0 + EXSLT

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:set="http://exslt.org/sets"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="exsl set str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<!-- first-pass -->
<xsl:variable name="categories">
    <xsl:for-each select="/Subjects/subject">
        <xsl:variable name="steps" select="str:tokenize(literal, ' / ')" />
        <xsl:for-each select="$steps" >
            <category>
                <xsl:attribute name="path">
                    <xsl:for-each select="preceding-sibling::token" >
                        <xsl:value-of select="concat(., '/')" />
                    </xsl:for-each>
                </xsl:attribute>
                <xsl:value-of select="." />
            </category>
        </xsl:for-each>
    </xsl:for-each>
</xsl:variable>
<xsl:variable name="category-set" select="exsl:node-set($categories)/category" />

<xsl:template match="/">
    <!-- output-->
    <nodes>
        <xsl:apply-templates select="set:distinct($category-set[not(string(@path))])"/>
    </nodes>
</xsl:template>

<xsl:template match="category">
    <node name="{.}" id="{generate-id()}">
        <xsl:apply-templates select="set:distinct($category-set[@path=concat(current()/@path, current(), '/')])"/>
    </node>
</xsl:template>

</xsl:stylesheet>

在支持所有这些扩展功能 (libxslt) 的处理器上运行,结果是:

<?xml version="1.0" encoding="UTF-8"?>
<nodes>
  <node name="ANTIQUES" id="idp4576">
    <node name="Americana" id="idp4704"/>
    <node name="Art" id="idp327680"/>
  </node>
  <node name="COOKING" id="idp25520">
    <node name="Beverages" id="idp25648">
      <node name="General" id="idp400976"/>
      <node name="Bartending" id="idp27984"/>
    </node>
  </node>
</nodes>

【讨论】:

  • 谢谢迈克尔。我只能使用 XSLT 1.0,所以无法使用您建议的解决方案。然而,我遵循了java中的代码逻辑,它就像一个魅力。谢谢:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多