我试图让我的头嵌套分组和排序,而使用for-each-group
。我的想法是先按producer
对item
s进行排序和分组。然后当我有这些producer
基团时,我想按code
对它们进行排序。然而,目前code
的顺序不像我想的那样工作。在下面的例子中,问题是item
和code=01001-064-03
。它应该与所有其他code
以01001
开头的item
组合在一起,但它不是。如果我将整个item/code[text()='01001-064-03']
(最后一个)移动到xml的开头,那么分组工作正常。
请问我有什么问题?
感谢<items>
<change_date>#11.11.2020 7:42:13</change_date>
<result>
<item>
<code>01001-064-01</code>
<producer>prod1</producer>
</item>
<item>
<code>01001-064-02</code>
<producer>prod1</producer>
</item>
<item>
<code>def</code>
<producer>prod1</producer>
</item>
<item>
<code>ghi</code>
<producer>prod2</producer>
</item>
<item>
<code>jkl</code>
<producer>prod3</producer>
</item>
<item>
<code>abc</code>
<producer>prod3</producer>
</item>
<item>
<code>def</code>
<producer>prod4</producer>
</item>
<item>
<code>ghi</code>
<producer>prod4</producer>
</item>
<item>
<code>jkl</code>
<producer>prod5</producer>
</item>
<item>
<code>01001-064-03</code>
<producer>prod1</producer>
</item>
</result>
</items>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:array="http://www.w3.org/2005/xpath-functions/array"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output method="xml" indent="yes" html-version="5"/>
<xsl:function name="mf:same-product" as="xs:boolean">
<xsl:param name="left" as="xs:string"/>
<xsl:param name="right" as="xs:string"/>
<xsl:variable name="leftParsed" select="mf:get-regexp-group($left, 1)"/>
<xsl:variable name="rightParsed" select="mf:get-regexp-group($right, 1)"/>
<xsl:sequence select="matches($leftParsed, $rightParsed)"/>
</xsl:function>
<xsl:function name="mf:get-regexp-group" as="xs:string">
<xsl:param name="text" as="xs:string"/>
<xsl:param name="groupNumber" as="xs:integer"/>
<xsl:variable name="result">
<xsl:analyze-string select="$text" regex="(^[a-zA-Z0-9]+)(.*)">
<xsl:matching-substring>
<xsl:value-of select="regex-group($groupNumber)"/>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:variable>
<xsl:sequence select="$result"/>
</xsl:function>
<xsl:template match="items">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="change_date"/>
<xsl:template match="result">
<data>
<xsl:for-each-group select="item" group-by="producer">
<xsl:sort select="producer"/>
<xsl:for-each-group select="current-group()" group-starting-with="item[not(mf:same-product(code, preceding-sibling::item[1]/code))]">
<xsl:sort select="code"/>
<group>
<xsl:apply-templates select="current-group()" />
</group>
</xsl:for-each-group>
</xsl:for-each-group>
</data>
</xsl:template>
<xsl:template match="item">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
我使用xslt2.0
与saxon-he 10.3
。
所以@michael。如果你需要更好的解释,我会尽力做到最好。
每个item
都是产品。这个产品有producer
和code
(产品代码)。我想把生产者的所有产品按code
分组。然而,类似产品的代码不相同,因此相似性由函数mf:same-product
匹配。例如,两个相似的产品可以是01001-064-01
和01001-064-02
,这里我检查第一个前缀01001
,如果它匹配,则意味着两个产品应该添加到同一组。
预期结果应该是这样的:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<group>
<item>
<code>01001-064-01</code>
<producer>prod1</producer>
</item>
<item>
<code>01001-064-02</code>
<producer>prod1</producer>
</item>
<item>
<code>01001-064-03</code>
<producer>prod1</producer>
</item>
</group>
<group>
<item>
<code>def</code>
<producer>prod1</producer>
</item>
</group>
<group>
<item>
<code>ghi</code>
<producer>prod2</producer>
</item>
</group>
<group>
<item>
<code>abc</code>
<producer>prod3</producer>
</item>
</group>
<group>
<item>
<code>jkl</code>
<producer>prod3</producer>
</item>
</group>
<group>
<item>
<code>def</code>
<producer>prod4</producer>
</item>
</group>
<group>
<item>
<code>ghi</code>
<producer>prod4</producer>
</item>
</group>
<group>
<item>
<code>jkl</code>
<producer>prod5</producer>
</item>
</group>
</data>
也许一个复合的group-by
就足够了:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-skip"/>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="change_date"/>
<xsl:template match="result">
<data>
<xsl:for-each-group select="item" composite="yes" group-by="producer, code => replace('[^a-z0-9].*$', '', 'i')">
<xsl:sort select="producer"/>
<xsl:sort select="code"/>
<group>
<xsl:apply-templates select="current-group()" />
</group>
</xsl:for-each-group>
</data>
</xsl:template>
<xsl:template match="item">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/ei5R4uT/10
这是Saxon 9.8及以后版本(例如Saxon 10)支持的XSLT 3,如果您确实需要使用XSLT 2.0处理器,那么嵌套的for-each-group group-by
或连接的分组键可以实现与上述XSLT 3中的composite
分组键相同的功能。
我的想法是先按
producer
对item
s进行排序和分组。然后,当我有这些producer
组时,我想按code
对它们进行排序。
如果这就是你想做的,为什么这样做还不够呢?
XSLT 3.0
<xsl:stylesheet version="3.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="result">
<xsl:for-each-group select="item" group-by="producer">
<xsl:sort select="producer"/>
<group>
<xsl:apply-templates select="current-group()">
<xsl:sort select="code"/>
</xsl:apply-templates>
</group>
</xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>
我使用
xslt 2.0
和saxon-he 10.3
。
实际上,您正在使用XSLT 3.0。