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