Select sibling records until next tag using XSLT 1.0

I have following input XML. The successive E_Records are optional and it should be populated into L_Record. I have written the below XSLT coding. Is there any changes should i have to do?

Input XML

<?xml version="1.0" encoding="UTF-8"?>
  <Record>
     <H_Record>
        <Rec_Type>H</Rec_Type>
     </H_Record>
     <C_Record>
        <Rec_Type>C</Rec_Type>
     </C_Record>
     <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>
     </L_Record>
     <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>3</E_Qty>
     </E_Record>
     <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>4</E_Qty>
     </E_Record>
     <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>
     </L_Record>
     <R_Record>
        <Rec_Type>R</Rec_Type>
     </R_Record>
  </Record>
  <Record>
     <H_Record>
        <Rec_Type>H</Rec_Type>
     </H_Record>
     <C_Record>
        <Rec_Type>C</Rec_Type>
     </C_Record>
     <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>
     </L_Record>
     <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>
     </L_Record>
     <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>1</E_Qty>
     </E_Record>
     <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>2</E_Qty>
     </E_Record>
     <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>
     </L_Record>
     <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>5</E_Qty>
     </E_Record>
     <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>6</E_Qty>
     </E_Record>
     <R_Record>
        <Rec_Type>R</Rec_Type>
     </R_Record>
  </Record>

The Output XML i am expecting is

 <Record>
   <H_Record>
        <Rec_Type>H</Rec_Type>
   </H_Record>
   <C_Record>
        <Rec_Type>C</Rec_Type>
   </C_Record>
   <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>
      <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>3</E_Qty>
      </E_Record>
      <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>4</E_Qty>
      </E_Record>
    </L_Record>
    <L_Record>
       <Rec_Type>L</Rec_Type>
       <L_Level>2</L_Level>
    </L_Record>
    <R_Record>
       <Rec_Type>R</Rec_Type>
    </R_Record>
  </Record>
  <Record>
     <H_Record>
        <Rec_Type>H</Rec_Type>
     </H_Record>
     <C_Record>
        <Rec_Type>C</Rec_Type>
     </C_Record>
     <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>
     </L_Record>
     <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>      
       <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>1</E_Qty>
       </E_Record>
       <E_Record>
        <Rec_Type>E</Rec_Type>
        <E_Qty>2</E_Qty>
       </E_Record>
     </L_Record>
     <L_Record>
        <Rec_Type>L</Rec_Type>
        <L_Level>2</L_Level>      
        <E_Record>
          <Rec_Type>E</Rec_Type>
          <E_Qty>5</E_Qty>
        </E_Record>
        <E_Record>
          <Rec_Type>E</Rec_Type>
          <E_Qty>6</E_Qty>
        </E_Record>
    </L_Record>
    <R_Record>
        <Rec_Type>R</Rec_Type>
     </R_Record>
  </Record>

I have written the XSLT mapping for this as below but i am not getting the required output. Could you please help me on this?

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output omit-xml-declaration="yes" indent="yes"/>
         <xsl:template match="@* | node()">
      <xsl:copy>
         <xsl:apply-templates select="@* | node()"/>
      </xsl:copy>
   </xsl:template>
   <xsl:template match="Record/L_Record">
   <L_Record>
    <xsl:variable name="header" select="."/>
            <xsl:apply-templates/>
            <xsl:if test = "not(following-sibling::L_Record)">
            <xsl:for-each select="following-sibling::E_Record[preceding-sibling::L_Record = $header]">
            <xsl:copy-of select="."/>
            </xsl:for-each>
            </xsl:if>
    </L_Record> 
    </xsl:template>
 </xsl:stylesheet>

Please help me on this? When i am executing the above code, Record 1 is working fine, but the record 2 is not working properly. The E-Record segment is not appearing in the L-Record segment.

Hi Please test this code:

  <?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="L_Record">
        <xsl:copy>
            <xsl:apply-templates/>
            <xsl:if test="following-sibling::*[1][self::E_Record]">
            <xsl:call-template name="Next_E_Record">
                <xsl:with-param name="next" select="following-sibling::*[1][self::E_Record]"></xsl:with-param>
            </xsl:call-template>
            </xsl:if>
        </xsl:copy>
    </xsl:template>

   <xsl:template name="Next_E_Record">
       <xsl:param name="next"/>
       <xsl:copy-of select="$next"/>
       <xsl:if test="$next/following-sibling::*[1][self::E_Record]">
           <xsl:call-template name="Next_E_Record">
               <xsl:with-param name="next" select="$next/following-sibling::*[1][self::E_Record]"></xsl:with-param>
           </xsl:call-template>
       </xsl:if>
   </xsl:template>

    <xsl:template match="E_Record"/>


</xsl:stylesheet>

See Transformation at https://xsltfiddle.liberty-development.net/bFWRApe

Ignoring Duplicate Elements - XSLT Cookbook [Book], Problem. You want to select all nodes that are unique in a given context based on uniqueness criteria. The following code produces a unique list of products from </xsl:stylesheet> If the elements are all siblings then use preceding- sibling. Do not sort to avoid considering all but the immediately preceding element. select="child::name/following-sibling::*" File: Data.xml <?xml version="1.0"?> <?xml-stylesheet type="application/xml" href="Transform.xslt"?> <people> <person born

One way would be to loop through the children of record until you find an L_Record then add siblings until you find another L_Record

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Record">
        <Record>
            <xsl:for-each select='*'>
                <xsl:choose>
                    <xsl:when test='name()="E_Record"'/>
                    <xsl:when test='name()="L_Record"'>
                        <L_Record>

                            <xsl:copy-of select="*"/>
                            <xsl:call-template name='next'>
                                <xsl:with-param name='current_L' select='.'/>
                                <xsl:with-param name='remainder' select='following-sibling::*'/>
                            </xsl:call-template>
                        </L_Record>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="."/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
        </Record>
    </xsl:template>

    <xsl:template name='next'>
        <xsl:param name='current_L'/>
        <xsl:param name='remainder'/>
        <xsl:param name='first' select='$remainder[1]'/>

        <xsl:if test='$remainder and not(name($first)="L_Record")'>
            <xsl:copy-of select="$first"/>

            <xsl:call-template name='next'>
                <xsl:with-param name='current_L' select='$current_L'/>
                <xsl:with-param name='remainder' select='$remainder[position() &gt; 1]'/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>

Processing Nodes by Position - XSLT Cookbook [Book], Use xsl:sort with the select set to the position( ) or last( ) functions. We compute some function of the first element that produces a value that we want to aggregate. Thus, the following two XSLT fragments are equivalent (the sort is redundant): </xsl:call-template> <xsl:value-of select="following-sibling::*[1]/@ name"/>� I'm sure this must be simple, but I don't understand how to count the number of sibling nodes, and then to do a basic previous/next navigation loop through these nodes, with the beginning node looping to the last (like a carousel).

You should be able to create an xsl:key matching E_Record elements and use the generated id of the first preceding L_Record sibling as the key.

Then you can match L_Record elements and copy the node set with the matching key.

Example...

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

  <xsl:key name="e_recs" match="E_Record" use="generate-id(preceding-sibling::L_Record[1])"/>

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

  <xsl:template match="L_Record">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <xsl:copy-of select="key('e_recs',generate-id())"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="E_Record"/>

</xsl:stylesheet>

Fiddle: http://xsltfiddle.liberty-development.net/94Acsmd

XSLT grouping techniques, <xsl:with-param name="node-set" select="following-sibling::*" /> (The input XML is assumed to be enclosed in <root> tag). But if lets say, But I can't seem a way to display the following siblings of the above, making the 3 cell rows. Should I� If a simple XPath is not able to find a complicated web element for our test script, we need to use the functions from XPath 1.0 library. With the combination of these functions, we can create more specific XPath.

Some interesting XSLT problems, template to construct an XPath expression, for a given element node --> <xsl: template <xsl:with-param name="x" select="$x/following-sibling::Record[1]" /> XSLT-1.0 doesn't have the grouping capability as that of XSLT-2.0 but there are many ways to achieve the desired. Let's consider the following simple XML with multiple <<item>> elements in <<items>>(root element) that have @type that is repeating among other <<item>> elements.

XSL Transformations (XSLT) Version 3.0, XSLT 3.0 is designed to be used in conjunction with XPath 3.0, which is The output of a transformation consists of the following: [Definition: The initial mode is the mode used to select template rules for Any whitespace text node whose immediate preceding-sibling node is an xsl:catch element is� <xsl:value-of> tag puts the value of the selected node as per XPath expression, as text. Declaration. Following is the syntax declaration of <xsl:value-of> element. <xsl:value-of select = Expression disable-output-escaping = "yes" | "no" > </xsl:value-of>

XSLT <xsl:apply-templates> Element, We can use the "select" attribute to specify in which order the child nodes are to be processed. Look at the following XSL style sheet: Example. <?xml version=" 1.0"� The result from the example above was a little disappointing; only one line of data was copied from the XML document to the output. In the next chapter you will learn how to use the <xsl:for-each> element to loop through the XML elements, and display all of the records.

Comments