2

I need to transform my xml to another xml based on parent and child relationship.

Below is my source xml

<FIXML>
    <Header>
        <RequestID>ReqID8942</RequestID>
    </Header>
    <Body>
     <Data>
      <LimitDetails>
        <LimitRefNo>L1</LimitRefNo>
        <LimitClassification>ROOT</LimitClassification>
        <ParentLimitRefNo></ParentLimitRefNo>
        <ApprovedLimit>100.0</ApprovedLimit>
      </LimitDetails>
      <LimitDetails>
        <LimitRefNo>L2</LimitRefNo>
        <LimitClassification>ClASSIFICATION1</LimitClassification>
        <ParentLimitRefNo>L1</ParentLimitRefNo>
        <ApprovedLimit>200.0</ApprovedLimit>
      </LimitDetails>
      <LimitDetails>
        <LimitRefNo>L3</LimitRefNo>
        <LimitClassification>CLASSIFICATION2</LimitClassification>
        <ParentLimitRefNo>L2</ParentLimitRefNo>
        <ApprovedLimit>300.0</ApprovedLimit>
      </LimitDetails>
      <LimitDetails>
        <LimitRefNo>L4</LimitRefNo>
        <LimitClassification>CLASSIFICATION3</LimitClassification>
        <ParentLimitRefNo>L3</ParentLimitRefNo>
        <ApprovedLimit>400.0</ApprovedLimit>
      </LimitDetails>
      </Data>
   </Body>
</FIXML>

Here,Child limits refers Parent limits based on ParentLimitRefNo. Parent limit is the one which has ParentLimitRefNo as empty.

Below is the xml which i need to produce based on source xml.

<FIXML>
    <Header>
        <RequestID>ReqID8942</RequestID>
    </Header>
    <Body>
     <Data>
      <LimitDetails>
        <Limit>
           <LimitRefNo>L1</LimitRefNo>
           <LimitClassification>ROOT</LimitClassification>
           <ParentLimitRefNo></ParentLimitRefNo>
           <ApprovedLimit>100.0</ApprovedLimit>
           <SubLimit>
             <LimitRefNo>L2</LimitRefNo>
             <LimitClassification>ClASSIFICATION1</LimitClassification>
             <ParentLimitRefNo>L1</ParentLimitRefNo>
             <ApprovedLimit>200.0</ApprovedLimit>
         <SubLimit>
           <LimitRefNo>L3</LimitRefNo>
               <LimitClassification>CLASSIFICATION2</LimitClassification>
               <ParentLimitRefNo>L2</ParentLimitRefNo>
               <ApprovedLimit>300.0</ApprovedLimit>
           <SubLimit>
              <LimitRefNo>L4</LimitRefNo>
              <LimitClassification>CLASSIFICATION3</LimitClassification>
                  <ParentLimitRefNo>L3</ParentLimitRefNo>
                  <ApprovedLimit>400.0</ApprovedLimit> 
           </SubLimit>
        </SubLimit>
       </SubLimit>
      </Limit>
    </LimitDetails>
  </Data>
</Body>

Thanks in advance.

2 Answers 2

1

This XSLT 2.0 transformation (easy to convert to XSLT 1.0):

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kLD" match="LimitDetails" use="ParentLimitRefNo"/>

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

 <xsl:template match="Data">
  <Data>
    <LimitDetails>
        <xsl:apply-templates
        select="LimitDetails[not(ParentLimitRefNo/node())]"/>
    </LimitDetails>
  </Data>
 </xsl:template>

 <xsl:template match="LimitDetails">
  <xsl:variable name="vSuf" select=
    "if(ParentLimitRefNo/text())
       then 'Sub'
       else ()
    "/>

  <xsl:element name="{$vSuf}Limit">
    <xsl:apply-templates select="node()|key('kLD', LimitRefNo)"/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<FIXML>
    <Header>
        <RequestID>ReqID8942</RequestID>
    </Header>
    <Body>
        <Data>
            <LimitDetails>
                <LimitRefNo>L1</LimitRefNo>
                <LimitClassification>ROOT</LimitClassification>
                <ParentLimitRefNo></ParentLimitRefNo>
                <ApprovedLimit>100.0</ApprovedLimit>
            </LimitDetails>
            <LimitDetails>
                <LimitRefNo>L2</LimitRefNo>
                <LimitClassification>ClASSIFICATION1</LimitClassification>
                <ParentLimitRefNo>L1</ParentLimitRefNo>
                <ApprovedLimit>200.0</ApprovedLimit>
            </LimitDetails>
            <LimitDetails>
                <LimitRefNo>L3</LimitRefNo>
                <LimitClassification>CLASSIFICATION2</LimitClassification>
                <ParentLimitRefNo>L2</ParentLimitRefNo>
                <ApprovedLimit>300.0</ApprovedLimit>
            </LimitDetails>
            <LimitDetails>
                <LimitRefNo>L4</LimitRefNo>
                <LimitClassification>CLASSIFICATION3</LimitClassification>
                <ParentLimitRefNo>L3</ParentLimitRefNo>
                <ApprovedLimit>400.0</ApprovedLimit>
            </LimitDetails>
        </Data>
    </Body>
</FIXML>

produces the wanted, correct result:

<FIXML>
   <Header>
      <RequestID>ReqID8942</RequestID>
   </Header>
   <Body>
      <Data>
         <LimitDetails>
            <Limit>
               <LimitRefNo>L1</LimitRefNo>
               <LimitClassification>ROOT</LimitClassification>
               <ParentLimitRefNo/>
               <ApprovedLimit>100.0</ApprovedLimit>
               <SubLimit>
                  <LimitRefNo>L2</LimitRefNo>
                  <LimitClassification>ClASSIFICATION1</LimitClassification>
                  <ParentLimitRefNo>L1</ParentLimitRefNo>
                  <ApprovedLimit>200.0</ApprovedLimit>
                  <SubLimit>
                     <LimitRefNo>L3</LimitRefNo>
                     <LimitClassification>CLASSIFICATION2</LimitClassification>
                     <ParentLimitRefNo>L2</ParentLimitRefNo>
                     <ApprovedLimit>300.0</ApprovedLimit>
                     <SubLimit>
                        <LimitRefNo>L4</LimitRefNo>
                        <LimitClassification>CLASSIFICATION3</LimitClassification>
                        <ParentLimitRefNo>L3</ParentLimitRefNo>
                        <ApprovedLimit>400.0</ApprovedLimit>
                     </SubLimit>
                  </SubLimit>
               </SubLimit>
            </Limit>
         </LimitDetails>
      </Data>
   </Body>
</FIXML>

Explanation:

  1. Using and modifying the identity rule.

  2. Using a key to specify all logical children of a LimitDetails from its LimitRefNo.


II. XSLT 1.0 solution:

This is an almost mechanical translation of the above transformation into XSLT 1.0 -- only the definition of the variable $vSuf is different:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kLD" match="LimitDetails" use="ParentLimitRefNo"/>

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

 <xsl:template match="Data">
  <Data>
    <LimitDetails>
        <xsl:apply-templates
        select="LimitDetails[not(ParentLimitRefNo/node())]"/>
    </LimitDetails>
  </Data>
 </xsl:template>

 <xsl:template match="LimitDetails">
  <xsl:variable name="vSuf" select=
  "concat('',
          substring('Sub',1 div boolean(ParentLimitRefNo/text()))
         )"/>

  <xsl:element name="{$vSuf}Limit">
    <xsl:apply-templates select="node()|key('kLD', LimitRefNo)"/>
  </xsl:element>
 </xsl:template>
</xsl:stylesheet>

When applied to the same XML document (above), the same correct result is produced.

Sign up to request clarification or add additional context in comments.

5 Comments

if i use XSLT1.0 solution which you guided me,i m getting four <Sublimit> open & closing tag but i need three <Sublimit> open and closing tag.pls help me.
if i use XSLT2.0 solution which you provided me, I m getting an error. ERROR: 'Syntax error in 'if(ParentLimitRefNo/text()) then 'Sub' else () '.' FATAL ERROR: 'Could not compile stylesheet'. I need one parent <Limit> tag and three <Sublimit> open and closing tag in the output.please help me.
@Moorthy, Your statement is not true as shown in the answer, the result contains exactly 3 SubLimit elements. As for getting an error "compiling the stylesheet", Are you sure that you really have an XSLT 2.0 processor? I always verify that my solutions run and produce the correct result. In fact, I just copy and paste the real result into the answer.
,I think i m using XSLT1.0 processor.I will update it XSLT2.0 and will update you soon.I m learing XSLT now based on your coding strategy.Thanks a lot.
@Moorthy, the author of the other answer has asked you to accept the best of the answers. Accepting is done by clicking the check-mark next to the answer. As only one of the answers can be accepted, and to help you making an informed decision, I want to highlight some advantages of using my code -- it is shorter, key-based -- that is much more efficient, and more DRY -- doesn't contain a single <xsl:if> or other XSLT conditional instruction. You might not have noticed, that I provide an XSLT 1.0 solution, too, and it has all plusses of its XSLT 2.0 cousin.
1

The use of keys surely is more elegant (see Dimitre Novatchevs solution), but this one takes the wanted changes in the structure into account (e.g. rename <LimitDetails /> to <SubLimit /> and placing the <Limit /> tag.):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

<xsl:template match="Data">
    <xsl:copy>
        <LimitDetails>
            <Limit>
                <xsl:apply-templates select=".//LimitDetails[./ParentLimitRefNo='']" />
            </Limit>
        </LimitDetails>
    </xsl:copy>
</xsl:template>

<xsl:template match="LimitDetails">
    <xsl:variable name="LimitRefNo" select="./LimitRefNo" />
    <xsl:apply-templates select="@*|node()" />
    <xsl:if test="../LimitDetails[./ParentLimitRefNo = $LimitRefNo]">
    <SubLimit>
        <xsl:apply-templates select="../LimitDetails[./ParentLimitRefNo = $LimitRefNo]" />
    </SubLimit>
    </xsl:if>
</xsl:template>
</xsl:transform>

Or as a modification of Dimitre's solution using keys:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kLD" match="LimitDetails" use="ParentLimitRefNo"/>

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

 <xsl:template match="Data">
  <Data>
    <LimitDetails>
      <Limit>
      <xsl:apply-templates select="LimitDetails[not(ParentLimitRefNo/node())]"/>
      </Limit>
    </LimitDetails>
  </Data>
 </xsl:template>

 <xsl:template match="LimitDetails">
  <xsl:apply-templates />
  <xsl:if test="key('kLD', LimitRefNo)">
  <SubLimit>
    <xsl:apply-templates select="key('kLD', LimitRefNo)"/>
  </SubLimit>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

13 Comments

There is a small issue. i m getting four </Sublimit> closing tag but there is only three <sublimit> open tag.pls help me.
is there any possibility to remove <SubLimit /> tag from output xml? <SubLimit><LimitRefNo>L4</LimitRefNo><ParentLimitRefNo>L3</ParentLimitRefNo><SubLimit /></SubLimit>
hielsnoppe, one of the two transformations in your answer produces compilation error and the second produces wrong result.
@DimitreNovatchev I'm highly interested in why you get an error, because when I test them using either PHP or Java both transformations work correctly, apart from what Moorthy mentioned above.
@Moorthy I just updated the transformation to account for what you mentioned above. Does it work for you?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.