Search code examples
xsltgroup-byxslt-2.0group

Group-by two conditions in one element


I am trying to group-by two conditions in one element, which practically means that machine and parts with same ID (before '-' should be grouped.

Input:

<Document>
    <DocumentLine>
        <ExternalBatchNo>KEC36770</ExternalBatchNo>
        <Attribute01>machine</Attribute01>
    </DocumentLine>
    <DocumentLine>
        <ExternalBatchNo>KEC36770-2</ExternalBatchNo>
        <Attribute01>part</Attribute01>
    </DocumentLine>
    <DocumentLine>
        <ExternalBatchNo>KEC36771</ExternalBatchNo>
        <Attribute01>machine</Attribute01>
    </DocumentLine>
    <DocumentLine>
        <ExternalBatchNo>KEC36771-2</ExternalBatchNo>
        <Attribute01>part</Attribute01>
    </DocumentLine>
    <DocumentLine>
        <ExternalBatchNo>KEC36771-3</ExternalBatchNo>
        <Attribute01>part</Attribute01>
    </DocumentLine>
</Document>

Expected result:

<Document>
    <DocumentLine>
        <ExternalBatchNo>KEC36770</ExternalBatchNo>
        <Attribute01>machine</Attribute01>
    </DocumentLine>
    <DocumentLine>
        <ExternalBatchNo>KEC36770-2</ExternalBatchNo>
        <Attribute01>part</Attribute01>
    </DocumentLine>
</Document>

<Document>
    <DocumentLine>
        <ExternalBatchNo>KEC36771</ExternalBatchNo>
        <Attribute01>machine</Attribute01>
    </DocumentLine>
    <DocumentLine>
        <ExternalBatchNo>KEC36771-2</ExternalBatchNo>
        <Attribute01>part</Attribute01>
    </DocumentLine>
    <DocumentLine>
        <ExternalBatchNo>KEC36771-3</ExternalBatchNo>
        <Attribute01>part</Attribute01>
    </DocumentLine>
</Document>

This is what I tried, but this results in an empty machine ID and so only groups on parts.

<xsl:for-each-group select="Message/Documents/Document/DocumentLine" group-by="substring-before(ExternalBatchNo,'-')">

So I am thinking about using some template where he should pick the ExternalBatchNo when value doesn't contain an '-'. But, how?


Solution

  • If you can use xslt version 2.0, you could use something like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet 
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="2.0">
      
      <xsl:output indent="yes"/>
      
      <xsl:strip-space elements="*"/>
      
      <xsl:template match="Documents">
        <xsl:copy>
          <!-- Your `substring-before` will not work on strings without the dash. Try tokenize instead -->
          <xsl:for-each-group select="Document/DocumentLine" group-by="tokenize(ExternalBatchNo,'-')[1]">
            <Document>
              <xsl:copy-of select="current-group()"/>
            </Document>
          </xsl:for-each-group>    
        </xsl:copy>
      </xsl:template>  
    
    </xsl:stylesheet>
    

    Using this source xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <Message>
      <Documents>
        <Document>
          <DocumentLine>
            <ExternalBatchNo>KEC36770</ExternalBatchNo>
            <Attribute01>machine</Attribute01>
          </DocumentLine>
          <DocumentLine>
            <ExternalBatchNo>KEC36770-2</ExternalBatchNo>
            <Attribute01>part</Attribute01>
          </DocumentLine>
          <DocumentLine>
            <ExternalBatchNo>KEC36771</ExternalBatchNo>
            <Attribute01>machine</Attribute01>
          </DocumentLine>
          <DocumentLine>
            <ExternalBatchNo>KEC36771-2</ExternalBatchNo>
            <Attribute01>part</Attribute01>
          </DocumentLine>
          <DocumentLine>
            <ExternalBatchNo>KEC36771-3</ExternalBatchNo>
            <Attribute01>part</Attribute01>
          </DocumentLine>
        </Document>    
      </Documents>
    </Message>
    

    will give this result:

    <?xml version="1.0" encoding="UTF-8"?>
    <Documents>
       <Document>
          <DocumentLine>
             <ExternalBatchNo>KEC36770</ExternalBatchNo>
             <Attribute01>machine</Attribute01>
          </DocumentLine>
          <DocumentLine>
             <ExternalBatchNo>KEC36770-2</ExternalBatchNo>
             <Attribute01>part</Attribute01>
          </DocumentLine>
       </Document>
       <Document>
          <DocumentLine>
             <ExternalBatchNo>KEC36771</ExternalBatchNo>
             <Attribute01>machine</Attribute01>
          </DocumentLine>
          <DocumentLine>
             <ExternalBatchNo>KEC36771-2</ExternalBatchNo>
             <Attribute01>part</Attribute01>
          </DocumentLine>
          <DocumentLine>
             <ExternalBatchNo>KEC36771-3</ExternalBatchNo>
             <Attribute01>part</Attribute01>
          </DocumentLine>
       </Document>
    </Documents>