当XPath本身是节点内的文本时,从节点获取值

时间:2011-04-13 07:48:58

标签: xslt xslt-1.0

简单问题:我有一个这样的XML文件:

<Locations>
    <Location>/Simulation/@ID</Location>
    <Location>/Simulation/Loans</Location>
    <Location>/Simulation/Assets</Location>
    <Location>/Simulation/BankAssets</Location>
    <Location>/Simulation/RealEstates</Location>
</Locations>

我还有第二个XML文件,其中包含与这些XPath节点匹配的数据。我需要创建一个XSLT 1.0而不需要任何可以将此数据文件转换为仅生成与这些节点匹配的数据列表的脚本。像这样:

<Data>
    <Item Node="/Simulation/@ID">
        <Value>1</Value>
        <Value>2</Value>
        <Value>3</Value>
    </Item>
    <Item Node="/Simulation/Loans">
        <Value>1024</Value>
        <Value>555</Value>
        <Value>0</Value>
    </Item>
</Data>

每个项目的Value节点数量并不重要。不同项目的价值之间的关系也不重要。基本上,样式表只是为了收集简单的统计数据来进行总结,平均等等。 根据用户的需求,位置列表变化很大。这个例子只是一些欺骗数据。

问题:如何收集这些信息?

1 个答案:

答案 0 :(得分:2)

实际上有两种方法可以做到这一点 - 一种是执行两阶段转换,另一种是使用evaluate扩展功能。

两阶段转型

首先,您需要根据Locations列表生成正确的XSL。它看起来像这样:

<xsl:template match="/">
    <Data>
        <Item Node="/Simulation/@ID">
            <xsl:apply-templates select="/Simulation/@ID"/>
        </Item>
        <Item Node="/Simulation/Loans">
            <xsl:apply-templates select="/Simulation/Loans"/>
        </Item>

        <!-- ... and so on ... -->

    </Data>
</xsl:template>

<xsl:template match="node()|@*">
    <Value>
        <xsl:value-of select="."/>
    </Value>
</xsl:template>

我认为创建基于XPath定义文件生成此输出的转换没有太多麻烦,因为模式非常简单。

下一步是将生成的转换应用于源文件。您可以将这两个步骤组合成小管道并获得所需的结果。

使用扩展功能

库EXSLT包含名为evaluate的扩展函数,它可以帮助您处理类似的情况。据我所知,Xalan变压器开箱即用,但萨克森不支持。 但是这个函数存在一个主要问题 - 从Xalan的2.7版本开始,有一个bug可以防止执行多个评估。不幸的是,当我尝试运行它时,下面的样式表会受到影响。如果可能的话,建议将Xalan的版本更改为2.6。尽管如此,这里的样式表可以满足您的需求而无需额外的生成阶段。

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dyn="http://exslt.org/dynamic" extension-element-prefixes="dyn">

    <xsl:variable name="locations" select="document('xpath.xml')/Locations"/>
    <xsl:variable name="root" select="/"/>

    <xsl:template match="/">
        <Data>
            <xsl:for-each select="$locations/Location">
                <Item Node="{.}">
                    <xsl:variable name="currentLocation" select="concat('$root', .)"/>
                    <xsl:apply-templates select="dyn:evaluate($currentLocation)"/>
                </Item>
            </xsl:for-each>
        </Data>
    </xsl:template>

    <xsl:template match="node()|@*">
        <Value>
            <xsl:value-of select="."/>
        </Value>
    </xsl:template>

</xsl:stylesheet>

假设具有位置定义的文档位于同一目录中,并称为xpath.xml。如果不是这种情况,请更新document()函数用法。