Grouping parent node based on child elements value

2019-08-06 09:53发布

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>

标签: xslt
1条回答
在下西门庆
2楼-- · 2019-08-06 10:43

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>
查看更多
登录 后发表回答