【问题标题】:missing first element in for-each loop with preceding-sibling in xslt缺少 for-each 循环中的第一个元素和 xslt 中的前同级
【发布时间】:2012-04-10 12:15:04
【问题描述】:

我在使用 XSLT 通过属性对 xml 条目进行分组时遇到问题。

这是我的源 xml:

<chron>
<chronEntry type="education" order="1" blockorder="1">
    <foo>bar</foo>
</chronEntry>
    <chronEntry type="education" order="2" blockorder="1">
<foo>bar</foo>
    </chronEntry>
<chronEntry type="education" order="3" blockorder="1">
    <foo>bar</foo>
</chronEntry>
<chronEntry type="communityservice" order="1" blockorder="2">
    <foo>bar</foo>
</chronEntry>
<chronEntry type="experience" order="1" blockorder="3">
    <foo>bar</foo>
</chronEntry>
<chronEntry type="experience" order="2" blockorder="3">
    <foo>bar</foo>
</chronEntry>
<chronEntry type="experience" order="3" blockorder="3">
    <foo>bar</foo>
</chronEntry>
<chronEntry type="experience" order="4" blockorder="3">
    <foo>bar</foo>
</chronEntry>
</chron>

我想要得到的是属性“类型”的所有可用值的列表。在这种情况下,它应该是: - 教育 - 社区服务 - 经验

我是这样尝试的:

<xsl:for-each select="/foobar/chron/chronEntry">
            <xsl:sort select="@blockorder"/>
                <xsl:if test ="@blockorder != preceding-sibling::chronEntry[1]/@blockorder">
                    <fo:table-row>
                        <fo:table-cell>
                            <fo:block><xsl:value-of select="@type"/></fo:block>
                        </fo:table-cell>
                    </fo:table-row>
                </xsl:if>
            </xsl:for-each>

我得到的是: - 社区服务 - 经验

我缺少“教育”(第一个)

我该怎么做才能得到它?

感谢您的帮助!

问候

戴夫

【问题讨论】:

  • 您使用的是 XSLT 1.0 还是 XSLT 2.0?

标签: xml xslt foreach xsl-fo


【解决方案1】:

问题在于,尽管您正在创建一个排序的节点列表,preceding-sibling::(或任何轴)只能用于表达文档中节点之间的关系(而不是节点列表)。

因此,上下文节点的preceding-sibling::chronEntry[1]selects the first preceding siblingchronEntry`在当前文档中 -- 不在排序的节点列表中。

解决方案

  1. 在 XSLT 1.0 中,在变量中捕获 xsl:for-each 的结果。由于这是臭名昭著的 RTF 类型,您必须使用正在使用的 XSLT 1.0 处理器支持的 xxx:node-set() 扩展函数将其转换为常规树。然后,在这棵常规树中,包括preceding-sibling:: 在内的坐标轴具有所需的含义。

  2. 推荐的解决方案。使用 Muenchian 分组:

像这样:

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

 <xsl:key name="kType" match="@type" use="."/>

 <xsl:template match=
  "chronEntry
    [generate-id(@type)
    =
     generate-id(key('kType', @type)[1])
    ]">
     <xsl:value-of select="concat(@type, ' ')"/>
 </xsl:template>
 <xsl:template match="text()"/>
</xsl:stylesheet>

当此转换应用于提供的 XML 文档时

<chron>
    <chronEntry type="education" order="1" blockorder="1">
        <foo>bar</foo>
    </chronEntry>
    <chronEntry type="education" order="2" blockorder="1">
        <foo>bar</foo>
    </chronEntry>
    <chronEntry type="education" order="3" blockorder="1">
        <foo>bar</foo>
    </chronEntry>
    <chronEntry type="communityservice" order="1" blockorder="2">
        <foo>bar</foo>
    </chronEntry>
    <chronEntry type="experience" order="1" blockorder="3">
        <foo>bar</foo>
    </chronEntry>
    <chronEntry type="experience" order="2" blockorder="3">
        <foo>bar</foo>
    </chronEntry>
    <chronEntry type="experience" order="3" blockorder="3">
        <foo>bar</foo>
    </chronEntry>
    <chronEntry type="experience" order="4" blockorder="3">
        <foo>bar</foo>
    </chronEntry>
</chron>

产生想要的正确结果

education communityservice experience 

【讨论】:

    【解决方案2】:

    你真的需要 xsl:sort 吗?还是你用它来分组?如果是这样,您可以删除 xsl:sort 并更正您的 xsl:if-test:

            <xsl:for-each select="/foobar/chron/chronEntry">
                <xsl:if test ="not(@blockorder = preceding-sibling::chronEntry/@blockorder)">
                    <fo:table-row>
                        <fo:table-cell>
                            <fo:block><xsl:value-of select="@type"/></fo:block>
                        </fo:table-cell>
                    </fo:table-row>
                </xsl:if>
            </xsl:for-each>
    

    如果你需要 xsl:sort,你可以使用这个:

            <xsl:variable name="types" select="/foobar/chron/chronEntry[not(preceding-sibling::*/@blockorder=@blockorder)]"/>
            <xsl:for-each select="$types">
                <xsl:sort select="@blockorder"/>
                <fo:table-row>
                    <fo:table-cell>
                        <fo:block>
                            <xsl:value-of select="@type"/>
                        </fo:block>
                    </fo:table-cell>
                </fo:table-row>
            </xsl:for-each>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-16
      • 2021-10-27
      • 2014-12-16
      • 1970-01-01
      相关资源
      最近更新 更多