Grouping parent node based on child elements value

2019-08-06 10:14发布

问题:

I need to transform XML using xslt.

Logic:

Split the parent if the parent having different child address and append the sequence number with parent name. Also need line number for the child. Here we may have number of parent nodes and each parent node may have more number of child nodes.

I have tried numerous ways to achieve this and I am stuck with generating sequence number in the foreach. So can any one try and give a solution for this.

Source XML as below:

<Data>
   <Parent>
      <Name>P1</Name>
      <Child>
        <Name>CName1</Name>
        <Address>Address1</Address>
      </Child>
      <Child>
        <Name>CName2</Name>
        <Address>Address2</Address>
      </Child>
      <Child>
        <Name>CName3</Name>
        <Address>Address1</Address>
      </Child>
   </Parent>

   <Parent>
     <Name>P2</Name>
       <Child>
         <Name>CName1</Name>
         <Address>Address1</Address>
       </Child>
   </Parent>
</Data>

The target XML should be as below:

<Data>
  <Parent>
     <Name>P1_1</Name>
     <Address>Address1</Address>
     <Child>
       <LineNumber>1</LineNumber>
       <Name>CName1</Name>
     </Child>
     <Child>
        <LineNumber>2</LineNumber>
        <Name>CName3</Name>
     </Child>
  </Parent>

  <Parent>
      <Name>P1_2</Name>
      <Address>Address2</Address>
      <Child>
         <LineNumber>1</LineNumber>
         <Name>CName2</Name>
      </Child>
  </Parent>

  <Parent>
      <Name>P2_1</Name>
      <Address>Address1</Address>
      <Child>
          <LineNumber>1</LineNumber>
          <Name>CName1</Name>
       </Child>
  </Parent>
</Data>

回答1:

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:key name="kAddress" match="Child" use="concat(generate-id(..), '|', Address)" />

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Parent">
    <xsl:variable name="parent" select="." />
    <xsl:for-each select="Child[
      generate-id()
      = 
      generate-id(key('kAddress', concat(generate-id($parent), '|', Address))[1])
    ]">
      <Parent>
        <Name><xsl:value-of select="concat(../Name, '_', position())" /></Name>
        <xsl:copy-of select="Address" />
        <xsl:apply-templates select="
          key('kAddress', concat(generate-id($parent), '|', Address))
        " />
      </Parent>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="Child">
    <xsl:copy>
      <xsl:copy-of select="@*" />
      <LineNumber><xsl:value-of select="position()" /></LineNumber>
      <xsl:apply-templates select="node()[not(self::Address)]" />
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Generates the following output for your sample:

<Data>
    <Parent>
        <Name>P1_1</Name>
        <Address>Address1</Address>
        <Child>
            <LineNumber>1</LineNumber>
            <Name>CName1</Name>
        </Child>
        <Child>
            <LineNumber>2</LineNumber>
            <Name>CName3</Name>
        </Child>
    </Parent>
    <Parent>
        <Name>P1_2</Name>
        <Address>Address2</Address>
        <Child>
            <LineNumber>1</LineNumber>
            <Name>CName2</Name>
        </Child>
    </Parent>
    <Parent>
        <Name>P2_1</Name>
        <Address>Address1</Address>
        <Child>
            <LineNumber>1</LineNumber>
            <Name>CName1</Name>
        </Child>
    </Parent>
</Data>


标签: xslt