jbrehr
jbrehr

Reputation: 815

XSLT XML to XML transform, while replacing and rearranging nodes

I have a large XML file which is structured like this:

<text n="1">
    <front>
      <title n="t1-1">A</title>
      <title n="t1-2">B</title>
    </front>
    <body>
      <p>
        <seg n="1-1">some <add>foo</add> text</seg>
        <seg n="1-2">some <add>foo</add>  <add>foo</add> text</seg>
        <seg n="1-3">some <add>foo</add> text</seg>
      </p>
    </body>
</text>
<text n="2">
    <front>
      <title n="t2-1">X</title>
      <title n="t2-2">Y</title>
    </front>
    <body>
      <p>
        <seg n="2-1">some <add>foo</add> text</seg>
        <seg n="2-2">some <add>foo</add> text</seg>
        <seg n="2-3">some text</seg>
      </p>
    </body>
</text>
<text>
  .....
</text>

I would like to transform it into a new XML document structured like this:

<document>
   <p n="1">
     <newtitle>A B</title>
     <seg n="1-1">some text</seg>
     <seg n="1-2">some text</seg>
     <seg n="1-3">some text</seg>
     <adds>
          <add>foo</add>
          <add>foo</add>
          <add>foo</add>
          <add>foo</add>
      </adds>
   </p>
   <p n="2">
      <newtitle>X Y</title>
      <seg n="2-1">some text</seg>
      <seg n="2-2">some text</seg>
      <seg n="2-3">some text</seg>
      <adds>
          <add>foo</add>
          <add>foo</add>
      </adds>
   </p>
   <p>
   ....
   </p>
</document>

I've tried several attempts using identity transform withxsl:for-each, but can't get it to extract and rearrange correctly.

Thanks in advance for any assistance.

Upvotes: 0

Views: 28

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167516

Here is an XSLT 3 solution (for XSLT 2 you need to spell out <xsl:mode on-no-match="shallow-copy"/> with the identity transformation template instead):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="3.0">

  <xsl:mode on-no-match="shallow-copy"/>

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

  <xsl:template match="root">
      <document>
          <xsl:apply-templates/>
      </document>
  </xsl:template>

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

  <xsl:template match="text/front">
      <newtitle>
          <xsl:value-of select="title"/>
      </newtitle>
  </xsl:template>

  <xsl:template match="text/body">
      <xsl:apply-templates select="p/seg"/>
      <adds>
          <xsl:apply-templates select="p/seg/add"/>
      </adds>
  </xsl:template>

  <xsl:template match="text/body/p/seg">
      <xsl:copy>
          <xsl:apply-templates select="@* | text()"/>
      </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

As you can see, by breaking up the task into template to transform each node into its target you get a well-structured approach.

Online sample at http://xsltfiddle.liberty-development.net/nbUY4kh/2

Upvotes: 1

Related Questions