Xml xsl/xpath以选择最多的同级,但不选择下一个类似同级

Xml xsl/xpath以选择最多的同级,但不选择下一个类似同级,xml,xslt,xpath,Xml,Xslt,Xpath,这是我在StackExchange的第一篇帖子,如果我做错了什么,请耐心等待: 我有一个从产品数据库派生的XML文件,其中除了元素的顺序之外,所有分组信息都丢失了。所有产品都有一个首先出现的article number元素,然后是未知数量的其他元素,直到下一个产品以一个新的article number元素开始,如下所示: <?xml version="1.0" encoding="ISO-8859-1"?> <Envelope> <body>

这是我在StackExchange的第一篇帖子,如果我做错了什么,请耐心等待:

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

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

但是,由于我需要匹配除ARTNO之外的其他(未知)元素,我尝试将其应用于我的案例中的尝试没有成功

我的非常简单的XSL(XSL 1)是基于我的假设,即应该能够获得下一个ARTNO元素之前的所有后续同级,但仅此而已(测试元素只是在尝试时)


我想我可以做一些非常难看的事情,循环整个结构,并通过使用position等解决它,但是我确信有更好的方法,我希望一些XSLT向导可以提供一些指导。非常感谢。

定义一个键
,然后在

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

在XSLT2.0中,使用
这个问题变得非常容易。因此,如果可以,请切换到XSLT2.0

在XSLT1.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时,你什么也不做,这终止了递归。

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

<?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 Sides”模式应用模板,应用模式的ARTNO模板有效地充当“出口”,而“*”照顾以下所有兄弟姐妹的第一个实例

这有点正确吗


谢谢你的帮助

一条评论-如果您的处理器是XSLT 1.0,那么您应该说
version=“1.0”
,而不是
version=“1.1”
,因为任何大于1.0的版本都会启用,这会抑制一些有用的错误检查。谢谢Ian,这是一个很好的建议,我打算遵循它。谢谢Michael(我是您的超级粉丝:-)你的例子做得很好,除了在上的一个小错误(当然应该是感谢)之外,还修复了这个错误。这看起来也很正确。我读过关于“明钦方法”的书,我对它有了大致的了解,但我永远也无法正确地理解它,尽管你的例子很明显有效,但我将不得不花一些时间来弄清楚它是如何工作的!谢谢@RichardRönnbäck这种方法本身并不是真正的慕尼黑分组,但这里的本质是,关键定义为您提供了一种方法来查找,给定一个特定的
ARTNO
,所有其他元素,这是最接近的前
ARTNO
,即介于此
ARTNO
和下一个
之间的所有元素(如果这是最后一个
ARTNO
,则为
products
元素的结尾)虽然这两种方法都使用键,但它并不是严格意义上的Muenchian分组。如果前面的
ARTNO
同级,它只是根据生成的id键非
ARTNO
元素,然后处理所有
ARTNO
元素,并通过键调用找到下面的同级。是的,我花了一些时间仔细考虑,一旦你找到了它它不仅很聪明,而且实际上非常先进。再次,非常感谢您的帮助!您已经了解了它的要点,尽管XSLT有时会让您绊倒。
node()
text()
comment()
processing-instruction()的测试
@*
*
具有最低的默认优先级,然后是
前缀:
@前缀:
,然后是特定元素或属性名称测试(
foo
@bar
)。涉及多个层次结构的测试(
foo/bar
*/*/
),等等)或者任何谓词的使用(
foo[5]
*[@id]
,…)都会击败简单的名称测试……但就是这样——任何包含
/
[]
的匹配都被认为具有相同的优先级,因此
*[@id]
foo/bar/baz/*/example[@kind='3']/node()
被认为比另一个更具体。的确,但多亏了像你这样的人,像我这样的人进步了:-)
<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"/>
<?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>