XSLT顺序和分组with for-each group



我试图让我的头嵌套分组和排序,而使用for-each-group。我的想法是先按produceritems进行排序和分组。然后当我有这些producer基团时,我想按code对它们进行排序。然而,目前code的顺序不像我想的那样工作。在下面的例子中,问题是itemcode=01001-064-03。它应该与所有其他code01001开头的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.0saxon-he 10.3

编辑:

所以@michael。如果你需要更好的解释,我会尽力做到最好。

每个item都是产品。这个产品有producercode(产品代码)。我想把生产者的所有产品按code分组。然而,类似产品的代码不相同,因此相似性由函数mf:same-product匹配。例如,两个相似的产品可以是01001-064-0101001-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分组键相同的功能。

我的想法是先按produceritems进行排序和分组。然后,当我有这些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.0saxon-he 10.3

实际上,您正在使用XSLT 3.0。

最新更新