xslt 顺序和分组与 for-each-group

问题描述

在使用 for-each-group 时,我正在尝试解决嵌套分组和排序问题。 我的想法是首先按 itemproducer 进行排序和分组。然后当我有这个 producer 组时,我想按 code 对每个组进行排序。但是,目前 code 的顺序并不像我想要的那样工作。在以下示例中,问题出在 itemcode=01001-064-03 上。它应该与所有其他 itemcode01001 开头但不是。如果我将整个 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>

fiddle example here

我将 xslt 2.0saxon-he 10.3 一起使用。


编辑:

所以@michael.hor257k 要求更好的解释,我会尽力做到最好:

每个 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 或连接的分组键可以实现与composite 上述 XSLT 3 中的分组键。

,

我的想法是首先按 itemproducer 进行排序和分组。然后当我有这个 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。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...