xsl / xpath 到 select 个兄弟姐妹,但不是下一个相似的兄弟姐妹
xsl / xpath to select siblings up to, but not next similar sibling
这是我第一次 post 在 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) 是基于我的假设,即应该能够让所有后续的兄弟姐妹都达到下一个 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 向导可以提供一些指导。将不胜感激。
定义一个键 <xsl:key name="group" match="products/*[not(self::ARTNO)]" use="generate-id(preceding-sibling::ARTNO[1])"/>
,然后在
中使用它
<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>
在 XSLT 2.0 中,使用 <xsl:for-each-group group-starting-with="ARTNO"/>
这个问题变得非常容易。因此,如果可以,请切换到 XSLT 2.0。
在 XSLT 1.0 中,我首选的方法是 "sibling recursion"。像这样:
<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 时,你什么都不做,这会终止递归。
为了完整起见,我post整个工作样式表:
<?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 模板有效地充当 "exit",“*”负责所有其他后续的第一个实例兄弟姐妹。
这样理解对吗?
感谢您的帮助!
这是我第一次 post 在 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) 是基于我的假设,即应该能够让所有后续的兄弟姐妹都达到下一个 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 向导可以提供一些指导。将不胜感激。
定义一个键 <xsl:key name="group" match="products/*[not(self::ARTNO)]" use="generate-id(preceding-sibling::ARTNO[1])"/>
,然后在
<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>
在 XSLT 2.0 中,使用 <xsl:for-each-group group-starting-with="ARTNO"/>
这个问题变得非常容易。因此,如果可以,请切换到 XSLT 2.0。
在 XSLT 1.0 中,我首选的方法是 "sibling recursion"。像这样:
<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 时,你什么都不做,这会终止递归。
为了完整起见,我post整个工作样式表:
<?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 模板有效地充当 "exit",“*”负责所有其他后续的第一个实例兄弟姐妹。
这样理解对吗?
感谢您的帮助!