【问题标题】:XPATH Axes - IssuesXPATH 轴 - 问题
【发布时间】:2011-08-13 06:37:04
【问题描述】:

XML 结构:

<units>
    <unit>
        <lesson>
            <name>Sample 1</name>
            <sections>
                <section type="IN">
                    <title> Sample Title 1 </title>
                </section>
            </sections>
        </lesson>
        <lesson>
            <name>Sample 2</name>
            <sections>
                <section type="OF">
                    <title> Sample Title 2 </title>
                </section>
            </sections>
        </lesson>
        <lesson>
            <name>Sample 3</name>
            <sections>
                <section type="IN">
                    <title> Sample Title 3</title>
                </section>
            </sections>
        </lesson>
        <lesson>
            <name>Sample 4</name>
            <sections>
                <section type="AS">
                    <title> Sample Title 4</title>
                </section>
            </sections>
        </lesson>
        <lesson>
            <name>Sample 5</name>
            <sections>
                <section type="IN">
                    <title> Sample Title 5</title>
                </section>
            </sections>
        </lesson>
    </unit>
</units>

我的要求是获取title元素的值并显示如下(对相似数据进行分组并显示)

IN:
Sample Title 1
Sample Title 3
Sample Title 5
OF:
Sample Title 2
AS:
Sample Title 5

我使用了以下兄弟选项来获得预期的输出。由于 XML 结构很大(我只粘贴了 sn-p),我无法使用 ../../ 对路径进行硬编码,并且全部都在 XSLT 中。请帮助我获得预期的输出。

【问题讨论】:

  • 使用“以下兄弟选项”进行分组的时间复杂度为 O(N^2) - 对于大型 XML 文档,这特别慢,就像您的情况一样。学习、理解和使用更有效的 Muenchian 分组方法。在此处阅读更多信息:jenitennison.com/xslt/grouping/muenchian.html

标签: xslt xpath axes


【解决方案1】:

使用分组解决这个问题比使用任何一个兄弟轴都好:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:key name="bySectionType" match="section" use="@type" />
    <xsl:template match="/">
        <xsl:apply-templates select="units/unit/lesson/sections/section" />
    </xsl:template>
    <xsl:template match="section" />
    <xsl:template
        match="section[generate-id()=
                       generate-id(key('bySectionType', @type)[1])]">
        <xsl:value-of select="concat(@type, ':&#xA;')" />
        <xsl:apply-templates select="key('bySectionType', @type)" mode="out" />
    </xsl:template>
    <xsl:template match="section" mode="out">
        <xsl:value-of select="concat(normalize-space(title), '&#xA;')" />
    </xsl:template>
</xsl:stylesheet>

输出:

IN:
Sample Title 1
Sample Title 3
Sample Title 5
OF:
Sample Title 2
AS:
Sample Title 4

为了完整起见,以下样式表使用 precedingfollowing 轴实现了相同的结果:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:template match="/">
        <xsl:apply-templates select="units/unit/lesson/sections/section" />
    </xsl:template>
    <xsl:template match="section" />
    <xsl:template 
        match="section[not(preceding::section[@type=current()/@type])]">
        <xsl:value-of select="concat(@type, ':&#xA;')" />
        <xsl:apply-templates select=".|following::section[@type=current()/@type]"
            mode="out" />
    </xsl:template>
    <xsl:template match="section" mode="out">
        <xsl:value-of select="concat(normalize-space(title), '&#xA;')" />
    </xsl:template>
</xsl:stylesheet>

这是一个效率低得多的解决方案。解决这个问题的正常方法是使用 Muenchian 方法进行分组,如上所示。

【讨论】:

  • @lwburk - 我们不能使用 XPATH Axes 实现这个结果吗?
  • @dirin - 是的,但是诀窍在于确定您何时位于具有给定类型的第一个节点,以便您只能输出该节点的标头。这需要您检查每个节点的兄弟姐妹,效率较低。 (由于嵌套,您的情况有些不同。您可能会使用preceding(而不是preceding-sibling)。)
  • @lwburk - 正如我之前提到的,在之前的帖子中粘贴的代码 sn-p 是原始 XML 文件的部分。我试图将您的代码合并到我的 XSLT 文件中。还有很多其他模板覆盖了这一点,我没有得到预期的输出。除了这个,你还有其他选择吗?请告诉我。
  • @dirin:您可以使用模式隔离@lwburk 的答案(即使在某些命名空间下使用模式!)。诸如 OF、AS 和 IN 之类的特定顺序意味着您提前知道这些键。那么你甚至不需要分组!
  • @dirin:请把你的新问题当作......新问题。你的问题得到了很好的答案——接受它,然后问另一个问题。 @lwburk、其他任何人,甚至 @Michael-Kay 本人都无法替代您必须进行的适当初始学习——在这方面,XSLT 与任何其他编程语言没有什么不同。
【解决方案2】:

这是 XSLT 2.0 中的解决方案:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" />
    <xsl:template match="/">
        <xsl:for-each-group select="//section" group-by="@type">
           <xsl:value-of select="@type, ':&#xa;'" separator=""/>
           <xsl:value-of select="current-group()/title" separator="&#xA;" />
           <xsl:value-of select="'&#xa;'"/>
        </xsl:for-each-group>              
    </xsl:template>
</xsl:stylesheet>

【讨论】:

  • 我们实际上使用的是 XSLT 1.0。这里提到的顺序不是升序或降序。我会要求输出显示在 OF、AS 和 IN(XML 可以按任何顺序包含这些信息)
猜你喜欢
  • 1970-01-01
  • 2011-06-21
  • 2010-10-07
  • 2013-10-15
  • 2011-02-21
  • 2011-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多