【问题标题】:xsl / xpath to select siblings up to, but not next similar siblingxsl / xpath 选择兄弟姐妹,但不是下一个相似的兄弟姐妹
【发布时间】:2015-04-01 06:24:45
【问题描述】:

这是我在 StackExchange 的第一篇文章,如果我做错了什么,请多多包涵:

我有一个从产品数据库派生的 XML 文件,其中所有分组信息都丢失了,除了元素的顺序。所有产品都有一个首先出现的商品编号元素,然后是未知数量的其他元素,直到下一个产品以新的商品编号元素开头,如下所示:

<?xml version="1.0" encoding="ISO-8859-1"?>
<Envelope>
    <body>
        <products>
            <ARTNO>10-0001</ARTNO>
            <LEVARTNO>K01-300</LEVARTNO>
            <EAN></EAN>
            <WEBGRUPP1>200</WEBGRUPP1>
            <ARTNO>10C0414</ARTNO>
            <LEVARTNO>0505-0906</LEVARTNO>
            <EAN></EAN>
            <WEBGRUPP1>701</WEBGRUPP1>
            <WEBGRUPP2></WEBGRUPP2>
        </products>
    </body>
</Envelope>

我需要将其重组为:

<?xml version="1.0" encoding="ISO-8859-1"?>
<Envelope>
    <body>
        <products>
            <Product>
                <ARTNO>10-0001</ARTNO>
                <LEVARTNO>K01-300</LEVARTNO>
                <EAN></EAN>
                <WEBGRUPP1>200</WEBGRUPP1>
            </Product>
            <Product>
                <ARTNO>10C0414</ARTNO>
                <LEVARTNO>0505-0906</LEVARTNO>
                <EAN></EAN>
                <WEBGRUPP1>701</WEBGRUPP1>
                <WEBGRUPP2></WEBGRUPP2>
            </Product>
        </products>
    </body>
</Envelope>

我已经尝试了几个小时来找到一个我可以理解的解决方案,但到目前为止还没有做到。我发现here 回答了一个非常相似的问题,但由于我需要匹配除 ARTNO 之外的其他(未知)元素,因此我尝试将其应用于我的案例没有成功。

我非常简单的 XSL (XSL 1) 是基于我的假设,即一个应该能够让所有后续兄弟姐妹 UP 到下一个 ARTNO 元素,但仅此而已(测试元素只是在尝试时)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.1">
    <xsl:output method="xml" indent="yes" encoding="UTF-8" name="xml"/>

    <xsl:template match="/">
        <Root>
            <Products>
                <xsl:for-each select="Envelope/body/products/*">
                    <xsl:apply-templates select="."/>
                </xsl:for-each>
            </Products>
        </Root>
    </xsl:template>


    <xsl:template match="node()">
        <xsl:apply-templates select="node()"/>
    </xsl:template>

    <xsl:template match="ARTNO">
        <Product>
            <xsl:copy-of select="."/>
            <Test>
                <xsl:copy-of select="following-sibling::ARTNO[1]"/>
                <!-- <xsl:value-of select="following-sibling::*[not(self::ARTNO)][2]"/> -->
            </Test>
        </Product>
    </xsl:template>

</xsl:stylesheet>

我想我可以做一些非常丑陋的事情,循环整个结构,并通过使用位置等来解决它,但我确信有更好的方法我希望一些 XSLT 向导可以提供一些指导。将不胜感激。

【问题讨论】:

  • 一条评论 - 如果您的处理器是 XSLT 1.0,那么您应该说 version="1.0" 而不是 version="1.1",因为任何大于 1.0 的版本都将启用 "forwards compatible" mode,这会抑制一些有用的错误检查位.
  • 谢谢伊恩,这是一个很好的建议,我打算遵循

标签: xml xslt xpath


【解决方案1】:

定义一个密钥&lt;xsl:key name="group" match="products/*[not(self::ARTNO)]" use="generate-id(preceding-sibling::ARTNO[1])"/&gt;,然后在

中使用它
    <xsl:template match="/">
        <Root>
            <Products>
                    <xsl:apply-templates select="Envelope/body/products/ARTNO"/>
            </Products>
        </Root>
    </xsl:template>

<xsl:template match="ARTNO">
  <Product>
    <xsl:copy-of select=". | key('group', generate-id())"/>
  </Product>
</xsl:template>

【讨论】:

  • 这似乎也能正常工作。我已经阅读了“Muenchian 方法”,并且大致了解了它,但我永远无法做到正确,尽管您的示例显然有效,但我将不得不花一些时间弄清楚它是如何工作的!谢谢!
  • @RichardRönnbäck 这种方法并不是真正的 Muenchian 分组,但这里的本质是键定义为您提供了一种查找方式,给定一个特定的 ARTNO,所有其他元素这是最接近的前面的ARTNO,即在这个ARTNO和下一个之间的所有那些(或者如果这是最后一个ARTNO,则在products元素的末尾)。
  • 这不是严格的 Muenchian 分组,尽管这两种方法都使用一个键。如果前面的ARTNO 同级,它只是通过生成的id 键非ARTNO 元素,然后处理所有ARTNO 元素并通过键调用找到以下同级。
  • 是的,我花了一些时间仔细考虑它,一旦你弄清楚它不仅聪明,而且实际上非常直截了当。再次感谢您的热心帮助!
【解决方案2】:

在 XSLT 2.0 中,使用&lt;xsl:for-each-group group-starting-with="ARTNO"/&gt; 可以很容易地解决这个问题。因此,如果可以,请切换到 XSLT 2.0。

在 XSLT 1.0 中,我首选的方法是“兄弟递归”。像这样的:

<xsl:template match="products">
  <xsl:apply-templates select="ARTNO"/>
</xsl:template>

<xsl:template match="ARTNO">
  <product>
    <xsl:copy-of select="."/>
    <xsl:apply-templates select="following-sibling::*[1]" mode="copy-siblings"/>
  </product>
</xsl:template>

<xsl:template match="*" mode="copy-siblings">
    <xsl:copy-of select="."/>
    <xsl:apply-templates select="following-sibling::*[1]" mode="copy-siblings"/>
</xsl:template>

<xsl:template match="ARTNO" mode="copy-siblings"/>

这个想法是,当您处理 ARTNO 或其以下同级之一时,您调用一个模板来复制该元素,然后移动到下一个同级;当你到达另一个 ARTNO 时,你什么也不做,这会终止递归。

【讨论】:

  • 谢谢迈克尔(我是你的忠实粉丝 :-) 你的例子运行良好,除了 ,当然应该是
  • 谢谢,修正了错字。
【解决方案3】:

为了完整起见,我发布了整个工作样式表:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" encoding="UTF-8" name="xml"/>

    <xsl:template match="/">
        <Root>
            <Products>
                <xsl:for-each select="Envelope/body/products/*">
                    <xsl:apply-templates select="."/>
                </xsl:for-each>
            </Products>
        </Root>
    </xsl:template>

    <xsl:template match="products">
        <xsl:apply-templates select="ARTNO"/>
    </xsl:template>

    <xsl:template match="ARTNO">
        <product>
            <xsl:copy-of select="."/>
            <xsl:apply-templates select="following-sibling::*[1]" mode="copy-siblings"/>
        </product>
    </xsl:template>

    <xsl:template match="*" mode="copy-siblings">
        <xsl:copy-of select="."/>
        <xsl:apply-templates select="following-sibling::*[1]" mode="copy-siblings"/>
    </xsl:template>

    <xsl:template match="ARTNO" mode="copy-siblings"/>

    <xsl:template match="node()">
        <xsl:apply-templates select="node()"/>
    </xsl:template>

</xsl:stylesheet>

作为一名自学成才的开发人员,有时我很难理解 XSL,所以让我确定我已经在一定程度上理解了正在发生的事情:

我知道在 XSL 中更具体的表达式优先,所以我认为当节点被处理时,匹配“ARTNO”的模板没有模式只是复制节点,然后应用模式为“copy-siblings”的模板,应用模式的 ARTNO 模板有效地充当“出口”,而“*”负责处理所有其他后续兄弟的第一个实例。

这样理解正确吗?

感谢您的帮助!

【讨论】:

  • 你已经掌握了它的要点,尽管 XSLT 的 rules for what counts as a "more specific expression" 有时会绊倒你。 node()text()comment()processing-instruction()@** 的测试具有最低的默认优先级,然后是 prefix:*@prefix:*,然后是特定元素或属性名称测试(foo@bar)。涉及多个层次结构(foo/bar*/* 等)或任何使用谓词(foo[5]*[@id]、...)的测试将优于普通名称测试。
  • ... 仅此而已 - 包含 /[]any 匹配被认为具有相同的优先级,因此 *[@id] 和 @987654340 都不是@ 被认为比另一个更具体。
  • 确实,但感谢像你这样的人,像我这样的人进步了:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-09
  • 2019-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多