Search code examples
xmlxsltattributesxslt-grouping

combine/group xml element attributes in one line


need help on how can I have all brand(?) in one line for each brands under the same record id?

input xml

<?xml version='1.0' encoding='UTF-8'?>
<ns1:Bags xmlns:ns1="test" id="3430">
  <ns1:inventory>
    <ns1:record id="3431" name="001">
      <ns1:brands>
        <ns1:brand brand1="kate spade" />
        <ns1:brand brand2="michael kors" />
        <ns1:brand brand3="coach" />
        <ns1:brand brand4="tory burch" />
        <ns1:brand brand5="dkny" />
      </ns1:brands>
    </ns1:record>
    <ns1:record id="3431" name="001">
      <ns1:brands>
        <ns1:brand brand1="calvin klein" />
        <ns1:brand brand2="fossil" />
        <ns1:brand brand3="tommy hilfiger" />
      </ns1:brands>
    </ns1:record>
    <ns1:record id="3435" name="002">
      <ns1:brands>
        <ns1:brand brand1="charles and keith" />
        <ns1:brand brand2="farfois" />
        <ns1:brand brand3="victoria's secret" />
      </ns1:brands>
    </ns1:record>
  </ns1:inventory>
</ns1:Bags>

output xml

<?xml version='1.0' encoding='UTF-8'?>
<ns1:Bags xmlns:ns1="test" id="3430">
  <ns1:inventory>
    <ns1:record id="3431" name="001">
      <ns1:brands>
        <ns1:brand brand1="kate spade" brand2="michael kors" brand3="coach" brand4="tory burch" brand5="dkny" />
        <ns1:brand brand1="calvin klein" brand2="fossil" brand3="tommy hilfiger" />
      </ns1:brands>
    </ns1:record>
    <ns1:record id="3435" name="002">
      <ns1:brands>
        <ns1:brand brand1="charles & keith" brand2="farfois" brand3="victoria's secret" />
      </ns1:brands>
    </ns1:record>
  </ns1:inventory>
</ns1:Bags>

tried to use grouping as advised but nothing happens and still not grouping the "record" by the same id. same with brand.

<xsl:template match="record">
    <xsl:for-each-group select="record" group-by="@id">
        <record id="{current-grouping-key()}">
        </record>
    </xsl:for-each-group>
</xsl:template>
</xsl:stylesheet>

also tried below for "brand" but no success

<xsl:template match="brand">
    <xsl:element name="{concat('brand1', @brand2, @brand3, @brand4, @brand5)}">
    </xsl:element>
</xsl:template>

Solution

  • Perhaps like this:

      <xsl:template match="inventory">
        <xsl:copy>
          <xsl:for-each-group select="record" group-by="@id">
            <xsl:copy>
              <xsl:apply-templates select="@*"/>
              <ns1:brands>
                <xsl:apply-templates select="current-group()/brands"/>
              </ns1:brands>
            </xsl:copy>
          </xsl:for-each-group>
        </xsl:copy>
      </xsl:template>
      
      <xsl:template match="brands">
        <brand>
          <xsl:apply-templates select="brand/@*"/>
        </brand>
      </xsl:template>
      
    

    Assumes identity copy with e.g. <xsl:mode on-no-match="shallow-copy"/> is in place.

    As for the namespaces you have added in the edit, see https://xsltfiddle.liberty-development.net/asoTKt/1 which declare xpath-default-namespace to allow for easy selection and also creates the result elements in the ns1 namespace:

    <?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"
    exclude-result-prefixes="xs"
    xpath-default-namespace="test"
    xmlns:ns1="test"
    version="3.0">
        
        <xsl:output indent="yes"/>
    
        <xsl:mode on-no-match="shallow-copy"/>
    
        <xsl:template match="inventory">
            <xsl:copy>
                <xsl:for-each-group select="record" group-by="@name">
                    <xsl:copy>
                        <xsl:apply-templates select="@*"/>
                        <ns1:brands>
                            <xsl:apply-templates select="current-group()/brands"/>
                        </ns1:brands>
                    </xsl:copy>
                </xsl:for-each-group>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="brands">
            <ns1:brand>
                <xsl:apply-templates select="brand/@*" />
            </ns1:brand>
        </xsl:template>
    
    </xsl:stylesheet>