Question

i asked smiliar question before Muenchian method for nested elements but this time the problem is different. my xml input is-

<?xml version="1.0" encoding="UTF-8"?> 
<foo>  <bar>bar</bar> 
       <bar>bar</bar> 
       <foobar>
               <baz>baz</baz>
               <baz>baz</baz>
      </foobar>
       <foobar>foobar</foobar>
</foo>

Output using xslt should be

 <?xml version="1.0" encoding="UTF-8"?>

 <foo>  
 <s> 
 <s> 
 <bar>bar</bar>  
 <bar>bar</bar>
 </s>
 <s> 
 <foobar>
 <s>
 <baz>baz</baz>
 <baz>baz</baz>
  </s>
</foobar>
 <foobar>foobar></foobar>
 </s> 
 </s>
</foo>

if any descendant element are in sequence the code should put them in s tag as well. the xslt file that puts the elements(not the descendant elements) is-

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

  <xsl:key name="kGroupLeader" match="*" use="generate-id(self::*[name() != name(preceding-sibling::*[1])])
   " />

<xsl:template match="*/">
<xsl:copy>
  <s>
    <xsl:for-each select="*[key('kGroupLeader', generate-id())]">
      <s>
        <xsl:copy-of select=". | following-sibling::*[
          name() = name(current())
          and
          generate-id(current()) = generate-id(
            preceding-sibling::*[key('kGroupLeader', generate-id())][1]
          )
        ]" />
      </s>
    </xsl:for-each>
  </s>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

I used descendant-or-self::* instead of * but did not get right output.What could be the solution. Many thanks.

Was it helpful?

Solution

Firstly, you could change the match on the root element, to a more generic match on any element with a child

 <xsl:template match="*[*]">

You wouldn't need the creation of the outer most s element in this case, but you would still iterate over the "group leaders" with xsl:for-each as before, but instead of doing xsl:copy-of you would use xsl:apply-templates so that the template would be recursively called, allowing it to be used on descendant elements.

Try this XSLT

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

    <xsl:key name="kGroupLeader" match="*" 
             use="generate-id(self::*[name() != name(preceding-sibling::*[1])])" />

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

    <xsl:template match="*[*]">
        <xsl:copy>
            <xsl:for-each select="*[key('kGroupLeader', generate-id())]">
              <s>
                <xsl:apply-templates select=". | following-sibling::*[
                      name() = name(current())
                      and generate-id(current()) = generate-id(
                        preceding-sibling::*[key('kGroupLeader', generate-id())][1]
                      )
                    ]" />
              </s>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Do note the use of the Identity Template here to copy all other nodes in the document.

EDIT: Note, I am not entirely sure this counts as Muenchian Grouping, as the key only contains the first element in each "group", rather than all elements that need to be surrounded by an s element.

Here is an alternate XSLT which is using Muenchian Grouping, with the consecutive elements which have the same name forming each group. (And the key uses the id of the first element in the group to look them up).

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

    <xsl:key name="kGroupLeader" match="*" 
             use="generate-id((self::*|preceding-sibling::*)[name() != name(preceding-sibling::*[1])][last()])" />

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

    <xsl:template match="*[*]">
        <xsl:copy>
            <xsl:for-each select="*[name() != name(preceding-sibling::*[1])]">
              <s>
                <xsl:apply-templates select="key('kGroupLeader', generate-id())" />
              </s>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top