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

XSLT - Selecting unique elements when multiple files

我不知道如何通过 xsltproc 使用两个文件来完成这项工作。 cooking.xml 使用 document() 打开,menu.xml 在命令行中传入。我可以 select 没有问题的食谱,我想不通的是如何获得独特的配料清单。当我在我的配料列表中使用前面的兄弟函数时,它的行为类似于 {[shell, beef, lettuce, tomato, cheese], [eggs, cheese]}。为什么像“cooking/recipe[@name = $menu]/ingredients”这样的 select 会创建一个我不能在其上使用 preceding-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 >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>

Why does a select like "cooking/recipe[@name = $menu]/ingredients" create a disjoint set that I can't use preceding-sibling on?

因为 preceding-sibling 轴是在输入树的上下文中计算的,而不是在 selection 的上下文中计算的。如果您 您的 selection 复制到一个变量,情况会有所不同。

无论哪种方式,使用 preceding-sibling 轴都不是 select 唯一值的好方法。如果您使用 xsltproc(即 libxslt),那么您可以这样做:

XSLT 1.0 + EXSLT set:distinct-值()

<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 文档。