XSLT - 在多个文件时选择唯一元素

Posted

技术标签:

【中文标题】XSLT - 在多个文件时选择唯一元素【英文标题】:XSLT - Selecting unique elements when multiple files 【发布时间】:2022-01-05 19:38:12 【问题描述】:

我不知道如何通过 xsltproc 使用两个文件来完成这项工作。 Cooking.xml 使用 document() 打开,menu.xml 在命令行中传入。我可以毫无问题地选择食谱,但我不知道如何获得一份独特的配料清单。当我在我的成分列表中使用前面的兄弟函数时,它的行为类似于 [shell, beef, lettuce,tomato, cheese], [eggs, cheese]。为什么像“cooking/recipe[@name = $menu]/ingredients”这样的选择会创建一个我不能使用previous-sibling的不相交集?

这是一个来自更大系统的人为示例。

文件cooking.xml

<?xml version="1.0" encoding="UTF-8"?>
<cooking  xmlns="https://cooking.com/2022/cooking">
    <recipe name="tacos">
    <ingredient name="shell"/> 
    <ingredient name="beef"/> 
    <ingredient name="lettuce"/>
    <ingredient name="tomato"/>
    <ingredient name="cheese"/>
    </recipe>
    <recipe name="hamburger">
    <ingredient name="bun"/> 
    <ingredient name="beef"/> 
    <ingredient name="lettuce"/>
    <ingredient name="tomato"/>
    </recipe>
    <recipe name="omelet">
    <ingredient name="eggs"/> 
    <ingredient name="cheese"/>
    </recipe>
    <recipe name="soup">
    <ingredient name="chicken"/> 
    <ingredient name="stock"/>
    </recipe>
</cooking>

文件 menu.xml

<?xml version="1.0" encoding="UTF-8"?>
<cooking xmlns="https://cooking.com/2022/cooking">
    <recipe name="tacos"/>
    <recipe name="omelet"/>
</cooking>

文件 shop.xsl

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:set="http://exslt.org/sets"
xmlns:cook="https://cooking.com/2022/cooking"
extension-element-prefixes="set">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>

<xsl:key name="rcp" match="recipe" use="@name" />

<xsl:template match="cooking">
    <output>
    <xsl:variable name="menu" select="recipe/@name" />
    <!-- switch context to target document in order to use key -->
    <xsl:for-each select="document('cooking.xml')">
        <xsl:for-each select="set:distinct(key('rcp', $menu)/ingredient/@name)">
            <ingredient name="."/>
        </xsl:for-each>
    </xsl:for-each>
    </output>
</xsl:template>
  
</xsl:stylesheet>

xsltproc shop.xsl menu.xml &gt;ingredients.xml

<?xml version="1.0" encoding="UTF-8"?>
<output xmlns:cook="https://cooking.com/2022/cooking"/>
 

期望的输出:

<?xml version="1.0" encoding="UTF-8"?>
<cooking xmlns:cook="https://cooking.com/2022/cooking">
    <ingredient name="shell"/> 
    <ingredient name="beef"/> 
    <ingredient name="lettuce"/>
    <ingredient name="tomato"/>
    <ingredient name="cheese"/>
    <ingredient name="eggs"/> 
</cooking>

【问题讨论】:

那么最小但完整的 XSLT 代码以及确切的不需要的结果示例和想要的结果示例在哪里? 【参考方案1】:

为什么像“cooking/recipe[@name = $menu]/ingredients”这样的选择会创建一个我不能使用previous-sibling的不相交集?

因为preceding-sibling 轴是在输入树的上下文中评估的,而不是在您选择的上下文中。如果您将选择复制到变量中,情况会有所不同。

无论哪种方式,使用preceding-sibling 轴都不是选择唯一值的好方法。如果您使用的是xsltproc(即libxslt),那么您可以执行以下操作:

XSLT 1.0 + EXSLT 设置:distinct-values()

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="set">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
  
<xsl:template match="/cooking">
    <output>
        <xsl:for-each select="set:distinct(document('cooking.xml')/cooking/recipe[@name = current()/recipe/@name]/ingredient/@name)">
            <ingredient name="."/>
        </xsl:for-each>
    </output>
</xsl:template>
  
</xsl:stylesheet>

或者效率更高一点:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="set">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>

<xsl:key name="rcp" match="recipe" use="@name" />

<xsl:template match="/cooking">
    <output>
        <xsl:variable name="menu" select="recipe/@name" />
        <!-- switch context to target document in order to use key -->
        <xsl:for-each select="document('cooking.xml')">
            <xsl:for-each select="set:distinct(key('rcp', $menu)/ingredient/@name)">
                <ingredient name="."/>
            </xsl:for-each>
        </xsl:for-each>
    </output>
</xsl:template>
  
</xsl:stylesheet>

得到:

结果

<?xml version="1.0" encoding="UTF-8"?>
<output>
  <ingredient name="shell"/>
  <ingredient name="beef"/>
  <ingredient name="lettuce"/>
  <ingredient name="tomato"/>
  <ingredient name="cheese"/>
  <ingredient name="eggs"/>
</output>

但是对于这个cooking.xml,必须有一个格式良好的 XML 文档。

【讨论】:

谢谢,这就是我要找的。我玩过 set:district() 但我没有正确使用它。我将把它应用到我的生产系统中,让你知道它是如何进行的。 生产数据上有一个命名空间。我已更新问题以包含命名空间。 见:***.com/a/34762628/3016153 我终于明白为什么我不能让它工作了。我的一个输入文件在命名空间中,另一个不在,但它使用相同的标签。对人类来说,它们看起来是一样的。当我将命名空间添加到第二个文件时,我能够让生产系统正常工作。 您不需要修改您的输入。您可以简单地使用前缀来处理一个输入文件中的元素,使用无前缀名称来处理另一个文件中的元素。

以上是关于XSLT - 在多个文件时选择唯一元素的主要内容,如果未能解决你的问题,请参考以下文章

jQuery:当我有多个具有相同名称但唯一 id 的元素时,我可以按名称选择一个元素并读取它的 id 吗?

从 Python 中的多个列表中仅选择一个唯一元素

XSLT 2.0 如何对每个唯一项目 id 的计数求和

从plist中的一组元素中获取唯一元素

如何在 XSLT 1.0 中获取具有唯一数字的数字?

xslt中count属性的范围是多少?