XSLT grouping for wordml

Tag: xslt Author: like1985619 Date: 2013-09-18

Please can anybody help to find solution?

Requirement - using xslt 1.0

I have a xml structure like

<w:p xmlns:w="http://foo.com">
    <w:r>run1</w:r>
    <w:r>run2</w:r>
    <w:r>runn3</w:r>
    <w:p>
        <w:r>para1</w:r>
    </w:p>
    <w:p>
        <w:r>para2</w:r>
    </w:p>
    <w:r>run4 - after para2</w:r>
    <w:r>run5- after para2</w:r>
   <w:p>
       <w:r>last para</w:r>
   </w:p>
</w:p>

Using xslt 1.0 I want output as follows:

<root>
 <w:p>
        <w:r>run1</w:r>
        <w:r>run2</w:r>
        <w:r>runn3</w:r>
        </w:p>
    <w:p>
        <w:r>para1</w:r>
    </w:p>
    <w:p>
        <w:r>para2</w:r>
    </w:p>
    <w:p>
        <w:r>run4 - after para2</w:r>
        <w:r>run5- after para2</w:r>
     </w:p>
    <w:p>
        <w:r>last para</w:r>
    </w:p>
</root>

basically i wanto group inside in sequence

XSLt which I have tried:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0" xmlns:w="http://foo.com" xmlns:msxsl="urn:schemas-microsoft-com:xslt">

   <xsl:template match="/">   
       <root>
        <!--<xsl:for-each select="w:p/*">
            <xsl:choose>
                <xsl:when test="name()='w:r'">
                    <w:p>
                        <xsl:copy-of select="."/>
                    </w:p>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="."/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each>-->
           <xsl:apply-templates select="w:r |w:p"/>
       </root>
   </xsl:template> 

    <xsl:template match="w:r">
        <w:p>
             <xsl:copy-of select="."/>
        </w:p>
    </xsl:template>

    <xsl:template match="w:p/w:p">
        <xsl:copy-of select="."/>
    </xsl:template>
</xsl:stylesheet>

Output I am getting:

<?xml version="1.0" encoding="UTF-16"?>
<root xmlns:w="http://foo.com" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
    <w:p>
        <w:r>run1</w:r>
    </w:p>
    <w:p>
        <w:r>run2</w:r>
    </w:p>
    <w:p>
        <w:r>runn3</w:r>
    </w:p>
    <w:p>
        <w:r>para1</w:r>
    </w:p>
    <w:p>
        <w:r>para2</w:r>
    </w:p>
    <w:p>
        <w:r>run4 - after para2</w:r>
    </w:p>
    <w:p>
        <w:r>run5- after para2</w:r>
    </w:p>
    <w:p>
        <w:r>last para</w:r>
    </w:p>
</root>

Best Answer

I would ignore the existing w:p elements altogether here. Ultimately what you want is a root element named root containing one w:p element for each run of adjacent w:r elements in the original source, regardless of whether or not those w:r elements were previously wrapped in a w:p. You can approach this using a technique I call sibling recursion - start with a template that fires for the first w:r in each group and then have it recurse to its immediately following sibling until you hit one that isn't another w:r.

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

  <xsl:template match="/">
    <root>
      <xsl:apply-templates mode="group"
          select=".//w:r[not(preceding-sibling::*[1][self::w:r])]" />
    </root>
  </xsl:template>

  <xsl:template match="w:r" mode="group">
    <w:p>
      <xsl:apply-templates select="." /><!-- applies non-"group" templates -->
    </w:p>
  </xsl:template>

  <xsl:template match="w:r">
    <xsl:copy-of select="."/>
    <xsl:apply-templates select="following-sibling::*[1][self::w:r]" />
  </xsl:template>
</xsl:stylesheet>

The magic is in the select expressions.

.//w:r[not(preceding-sibling::*[1][self::w:r])]

selects all w:r elements that do not immediately follow another w:r (i.e. the first one in each contiguous run).

following-sibling::*[1][self::w:r]

selects the immediately following sibling element of the current element, but only if it is a w:r. The expression will select nothing if either there isn't a following sibling element at all, or there is one but it isn't a w:r.

comments:

:ohhh its brilliant...It generates expected output and solved my problem.. Thanks a lot :)
@user2846094 glad it helped, and welcome to Stack Overflow. When you get an answer that solves your problem it's good manners to accept the answer by clicking on the tick mark to the left. As well as rewarding the answerer this shows other readers at a glance that the problem is solved.
ohhh sorry. i was unaware of the process. this is my first question on stack overflow though i always refer it to find answers. i accepted the answer. thanks a lot again