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

发布于 2024-10-31 15:57:47 字数 1004 浏览 0 评论 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>

每个项目的值节点数量并不重要。不同项目的值之间的关系也不重要。基本上,样式表只是为了收集简单的统计数据以进行总结、平均等等。 货车的位置列表有很大差异,具体取决于用户的需求。这个例子只是一些欺骗数据。

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

Simple problem: I have an XML file like this:

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

I also have a second XML file containing data which matches these XPath nodes. And I need to create an XSLT 1.0 without any scripting that can transform this data file to just generate a list of data that matches these nodes. Something like this:

<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>

The number of Value nodes per item is unimportant. Nor is the relation between the values of different items important. Basically, the stylesheet is just meant to collect simple, statistical data to be summed up, averaged and whatever more.
The list of locations van vary a lot, depending on what the user wants. This example is just some spoof data.

Question: how to collect this information?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

送舟行 2024-11-07 15:57:47

实际上有两种方法可以做到这一点 - 一种是执行两阶段转换,另一种是使用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 变压器支持它,但 Saxon 不支持它。
然而,此功能存在一个主要问题 - 从 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() 函数用法。

There are actually two ways of doing this - one is to perform two-phase transformation and another one is to use evaluate extension function.

Two-phase transformation

First you need to generate correct XSL based on your Locations list. It could look like this:

<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>

I assume you don't have much troubles with creating transformation that will produce this output based on your XPath definition file as pattern is pretty simple.

The next step is to apply generated transformation to your source file. You could combine these two steps into small pipe and get results you want.

Using extension function

Library EXSLT contains extension function called evaluate which helps in cases like yours. It's supported by Xalan transformer out of the box but not by Saxon as far as I know.
There is however major issue with this function - starting with version 2.7 of Xalan there is a bug which prevents execution of multiple evaluations. Unfortunately stylesheet below is affected when I tried to run it. Suggestion would be to change version of Xalan to 2.6 if possible. Nevertheless, here's the stylesheet that will do what you want without additional generation phase.

<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>

Assumption is that the document with Locations definitions is on a same directory and is called xpath.xml. Update document() function usage if it's not the case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文