0

I need to transform an existing XML structure to another XML structure.

XMLSource:

 <?xml version="1.0"?>
  <content>
                <first>Paragraph-1</first>
                <comment>Comment-1</comment>
                <likes>like-1</likes>

                <first>Paragraph-2</first>
                <comment>Comment-2</comment>
                <likes>like-2</likes>

                <first>Paragraph-3</first>
                <comment>Comment-3</comment>

 </content>

Output Format Needed:

<content1>
 <block>
  <aaa>Paragraph-1</aaa>
  <bbb>Comment-1</bbb>
  <ccc>like-1</ccc>
 </block>
 <block>
  <aaa>Paragraph-2</aaa>
  <bbb>Comment-2</bbb>
  <ccc>like-2</ccc>
 </block>
 <block>
  <aaa>Paragraph-3</aaa>
  <bbb>Comment-3</bbb>
 </block>
</content1>

How this can be done using XSLT.

3
  • Can you use XSLT 2.0 (as implemented by Saxon 9 and AltovaXML and others)? In that case you easily solve that with <xsl:template match="content"><xsl:for-each-group select="*" group-starting-with="first">...</xsl:for-each-group>. I can post a complete example if you tell us that you can use XSLT 2.0. Commented Mar 8, 2012 at 13:37
  • Thanks for your response. If the "first,comment,like" elements are not mandatory then how to implement the same, as i observe that the code is based on 'first' element occurance, if it is not mandatory one. for suppose consider the input xml<?xml version="1.0"?> <content><comment>Comment-1</comment><likes>like-1</likes><first>Paragraph-2</first> <comment>Comment-2</comment> <likes>like-2</likes> <first>Paragraph-3</first> <comment>Comment-3</comment> </content> Commented Mar 12, 2012 at 15:36
  • And output should be like <content1> <block> <bbb>Comment-1</bbb> <ccc>like-1</ccc> </block> <block> <aaa>Paragraph-2</aaa> <bbb>Comment-2</bbb> <ccc>like-2</ccc> </block> <block> <aaa>Paragraph-3</aaa> <bbb>Comment-3</bbb> </block> </content1> Commented Mar 12, 2012 at 15:37

3 Answers 3

2

Please observe robust XML below, and let me know if you need any more suggestions. :) I have provided more than what you had asked.

INPUT XML:

<?xml version="1.0" encoding="utf-8"?>
    <content>
      <first>Paragraph-1</first>
      <likes>like-1</likes>

      <first>Paragraph-2</first>
      <comment>Comment-2</comment>
      <likes>like-2</likes>

      <first>Paragraph-3</first>
      <comment>Comment-3</comment>

      <first>Paragraph-4</first>
      <comment>Comment-4</comment>
      <likes>like-4</likes>
    </content>

XSLT CODE:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
    <xsl:output method="xml" indent="yes"/>

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

  <xsl:template match="content">
    <xsl:element name="content1">
      <xsl:apply-templates select="node()|@*"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="first">
    <xsl:variable name="count" select="count(following-sibling::first)"/>
    <xsl:element name="block">
      <aaa>
        <xsl:apply-templates select="node()|@*"/>
      </aaa>
      <xsl:for-each select="following-sibling::comment[1][count(following-sibling::first) = $count]">
        <bbb>
          <xsl:apply-templates select="node()|@*"/>
        </bbb>
      </xsl:for-each>
      <xsl:for-each select="following-sibling::likes[1][count(following-sibling::first) = $count]">
        <ccc>
          <xsl:apply-templates select="node()|@*"/>
        </ccc>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
  <xsl:template match="comment|likes"/>

</xsl:stylesheet>

Output XML:

<?xml version="1.0" encoding="utf-8"?>
<content1>
  <block>
    <aaa>Paragraph-1</aaa>
    <ccc>like-1</ccc>
  </block>

  <block>
    <aaa>Paragraph-2</aaa>
    <bbb>Comment-2</bbb>
    <ccc>like-2</ccc>
  </block>

  <block>
    <aaa>Paragraph-3</aaa>
    <bbb>Comment-3</bbb>
  </block>

  <block>
    <aaa>Paragraph-4</aaa>
    <bbb>Comment-4</bbb>
    <ccc>like-4</ccc>
  </block>
</content1>

This works like gem!!!!!! :D

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

Comments

1

Here is an XSLT 2.0 example:

<xsl:stylesheet
  version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>

  <xsl:param name="start-name" select="'a'"/>
  <xsl:param name="nchars" select="3"/>
  <xsl:variable name="start-index" select="string-to-codepoints($start-name)"/>

  <xsl:template match="content">
    <content1>
      <xsl:for-each-group select="*" group-starting-with="first">
        <block>
          <xsl:apply-templates select="current-group()"/>
        </block>
      </xsl:for-each-group>
    </content1>
  </xsl:template>

  <xsl:template match="content/*">
    <xsl:variable name="pos" select="position()"/>
    <xsl:element name="{string-join(for $c in 1 to $nchars return codepoints-to-string($pos - 1 + $start-index), '')}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

</xsl:stylesheet>

Comments

0

Here's another XSLT 1.0 option that I prefer because everything has it's own template. I think that this makes it much easier to increase the complexity of the stylesheet than using multiple xsl:for-each's.

XML Input

<content>
  <first>Paragraph-1</first>
  <likes>like-1</likes>

  <first>Paragraph-2</first>
  <comment>Comment-2</comment>
  <likes>like-2</likes>

  <first>Paragraph-3</first>
  <comment>Comment-3</comment>

  <first>Paragraph-4</first>
  <comment>Comment-4</comment>
  <likes>like-4</likes>
</content>

XSLT 1.0

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

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

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

  <xsl:template match="first">
    <block>
      <aaa>
        <xsl:apply-templates select="node()|@*"/>
      </aaa>
      <xsl:apply-templates select="following-sibling::*[1][name()='comment']" mode="mod"/>
      <xsl:apply-templates select="following-sibling::*[1][name()='likes']" mode="mod"/>      
    </block>
  </xsl:template>

  <xsl:template match="comment" mode="mod">
    <bbb>
      <xsl:apply-templates select="node()|@*"/>
    </bbb>
    <xsl:apply-templates select="following-sibling::*[1][name()='likes']" mode="mod"/>
  </xsl:template>

  <xsl:template match="likes" mode="mod">
    <ccc>
      <xsl:apply-templates select="node()|@*"/>
    </ccc>
  </xsl:template>

  <xsl:template match="comment|likes"/>

</xsl:stylesheet>

XML Output

<content1>
   <block>
      <aaa>Paragraph-1</aaa>
      <ccc>like-1</ccc>
   </block>
   <block>
      <aaa>Paragraph-2</aaa>
      <bbb>Comment-2</bbb>
      <ccc>like-2</ccc>
   </block>
   <block>
      <aaa>Paragraph-3</aaa>
      <bbb>Comment-3</bbb>
   </block>
   <block>
      <aaa>Paragraph-4</aaa>
      <bbb>Comment-4</bbb>
      <ccc>like-4</ccc>
   </block>
</content1>

Comments

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.