xpath获取祖先节点的位置

时间:2014-10-21 07:08:02

标签: xslt xpath xslt-2.0

我有一个类似以下的XML结构:

<inventory>
    <location>
        <category>
            <children>
                <book>
                    <title>Harry Potter</title>
                    <price>$28.50</price>
                </book>
                <cd>
                    <title>Frozen</title>
                    <price>12.8</price>
                </cd>
            </children>
        </category>
        <category>
            <adult>
                <book>
                    <title>Da vinci code</title>
                    <price>32.50</price>
                </book>
                <cd>
                    <title>Da vinci code</title>
                    <price>13.80</price>
                </cd>
            </adult>
        </category>
    </location>
    <location>
        <category>
            <cooking>
                <book>
                    <title>everyday Italian</title>
                    <price>30.50</price>
                </book>
            </cooking>
        </category>
    </location>
</inventory>

我想要打印的是:

Location category# category  title       price
1        1         children  Harry       28.50
2        1         cooking   everyday... 30.50
1        2         cd        Da vinci code 13.8

如果我目前在每个<title>元素上,我如何获得<location><category>的位置?

我尝试过的事情:

count(ancestor::location/preceding-silbing::location) + 1
count(ancestor::category/preceding-silbing::category) + 1

但它们都不起作用。

2 个答案:

答案 0 :(得分:1)

由于您使用的是XSLT 2.0,请尝试使用功能更强大的xsl:number代替count()

位置:

<xsl:number select="ancestor::location"/>

类别:

<xsl:number select="ancestor::category"/>

这是一个更详细的例子。 (注意,这只是为了说明目的;它可能不是最有效的例子。)

XML输入

<inventory>
    <location>
        <category>
            <children>
                <book>
                    <title>Harry Potter</title>
                    <price>$28.50</price>
                </book>
                <cd>
                    <title>Frozen</title>
                    <price>12.8</price>
                </cd>
            </children>
        </category>
        <category>
            <adult>
                <book>
                    <title>Da vinci code</title>
                    <price>32.50</price>
                </book>
                <cd>
                    <title>Da vinci code</title>
                    <price>13.80</price>
                </cd>
            </adult>
        </category>
    </location>
    <location>
        <category>
            <cooking>
                <book>
                    <title>everyday Italian</title>
                    <price>30.50</price>
                </book>
            </cooking>
        </category>
    </location>
</inventory>

XSLT 2.0

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:local="local">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>

    <xsl:variable name="column-widths">
        <cols>
            <col name="location" width="10"/>
            <col name="categorynbr" width="11"/>
            <col name="category" width="{max((string-length('category'),//category/*/string-length(name())))+2}"/>
            <col name="title" width="{max((string-length('title'),//category/*/book/title/string-length(normalize-space())))+2}"/>
            <col name="price" width="{max((string-length('price'),//category/*/book/price/string-length(normalize-space())))+2}"/>
        </cols>
    </xsl:variable>

    <xsl:function name="local:padValue">
        <xsl:param name="colname"/>
        <xsl:param name="value"/>
        <xsl:variable name="padding">
            <xsl:for-each select="1 to xs:integer($column-widths/*/col[@name=$colname]/@width) - string-length($value)">
                <xsl:text> </xsl:text>
            </xsl:for-each>                    
        </xsl:variable>
        <xsl:value-of select="concat($value,$padding)"/>
    </xsl:function>

    <xsl:template match="/*">
        <xsl:value-of select="concat(local:padValue('location','Location'),
            local:padValue('categorynbr','category#'),
            local:padValue('category','category'),
            local:padValue('title','title'),
            local:padValue('price','price'),'&#xA;')"/>
        <xsl:apply-templates select="*/*/*/book">
            <xsl:sort>
                <xsl:number select="ancestor::category"/>
            </xsl:sort>
        </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="book">
        <xsl:variable name="loc">
            <xsl:number select="ancestor::location"/>            
        </xsl:variable>
        <xsl:variable name="cat">
            <xsl:number select="ancestor::category"/>            
        </xsl:variable>
        <xsl:value-of select="concat(local:padValue('location',$loc),
            local:padValue('categorynbr',$cat),
            local:padValue('category',../local-name()),
            local:padValue('title',normalize-space(title)),
            local:padValue('price',normalize-space(price)),'&#xA;')"/>
    </xsl:template>

</xsl:stylesheet>

<强>输出

Location  category#  category  title             price   
1         1          children  Harry Potter      $28.50  
2         1          cooking   everyday Italian  30.50   
1         2          adult     Da vinci code     32.50   

答案 1 :(得分:0)

我的假设是,这就是你所追求的:

您的上下文节点是输入文档中的title元素之一。现在你想知道

  • 您的上下文节点包含在其父location元素中的inventory元素的(从一开处)索引

  • 您的上下文节点包含在其父category元素中的category元素的(从一开处)索引

对于提供的XML输入,可以使用XPath表达式

实现
count(ancestor::location/preceding-sibling::location) + 1

count(ancestor::category/preceding-sibling::category) + 1

确保轴和节点测试由两个冒号分隔(通过编辑校正)。另外,请确保preceding-sibling拼写正确。

可以在位置步骤[1]ancestor::location之后添加谓词ancestor::category,以明确说明您在第一个祖先倒退后文件顺序(即最近的祖先)。如果XML包含嵌套的locationcategory元素,那么必需