Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using XSL to replace XML nodes with new nodes

Tags:

xml

xslt

xpath

I need an XSL solution to replace XML nodes with new nodes.

Say I have the following existing XML structure:

<root>
    <criteria>
        <criterion>AAA</criterion>
    </criteria>
</root>

And I want to replace the one criterion node with:

<criterion>BBB</criterion>
<criterion>CCC</criterion>
<criterion>DDD</criterion>

So that the final XML result is:

<root>
    <criteria>
        <criterion>BBB</criterion>
        <criterion>CCC</criterion>
        <criterion>DDD</criterion>
    </criteria>
</root>

I have tried using substring-before and substring-after to just copy the first half of the structure, then just copy the second half (in order to fill in my new nodes in between the two halves) but it appears that the substring functions only recognize text in between the nodes' tags, and not the tags themselves like I want them to. :( :(

Any other solutions?

like image 828
developer Avatar asked Jun 01 '10 19:06

developer


People also ask

Can we convert XML to xsl?

Execute an XSLT transformation from an XML file Associate an XSLT style sheet with the XML document. Add an xml-stylesheet processing instruction to the XML document. For example, add the following line to the document prolog: <? xml-stylesheet type='text/xsl' href='filename.

How does xsl work with XML?

XSLT is used to transform XML document from one form to another form. XSLT uses Xpath to perform matching of nodes to perform these transformation . The result of applying XSLT to XML document could be an another XML document, HTML, text or any another document from technology perspective.

How does XSLT transform XML?

XSLT is commonly used to convert XML to HTML, but can also be used to transform XML documents that comply with one XML schema into documents that comply with another schema. XSLT can also be used to convert XML data into unrelated formats, like comma-delimited text or formatting languages such as troff.


2 Answers

XSL cannot replace anything. The best you can do is to copy the parts you want to keep, then output the parts you want to change instead of the parts you don't want to keep.


Example:

<?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"/>

    <!-- This is an identity template - it copies everything
         that doesn't match another template -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

  <!-- This is the "other template". It says to use your BBB-DDD elements
       instead of the AAA element -->
  <xsl:template match="criterion[.='AAA']">
    <xsl:element name="criterion">
      <xsl:text>BBB</xsl:text>
    </xsl:element>
    <xsl:element name="criterion">
      <xsl:text>CCC</xsl:text>
    </xsl:element>
    <xsl:element name="criterion">
      <xsl:text>DDD</xsl:text>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

The template match @* | node() matches any attribute or any other kind of node. The trick is that template matches have priorities. You can think of the rule as being "the more specific match wins". Anything is going to be more specific than "any attribute or other node". This makes the "identity" match a very low priority.

When it is matched, it simply copies any nodes it finds inside the matched attribute or node.

Any other templates you have will have a higher priority. Whatever they match, it's the code inside the more specific template that will have effect. For example, if you simply removed everything inside of the criterion[.='AAA'] template, you'd find that you had copied your input exactly, except for the "AAA" element.

like image 151
John Saunders Avatar answered Sep 18 '22 10:09

John Saunders


Here is one correct solution, which is probably one of the shortest:

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

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

 <xsl:template match="criterion[. = 'AAA']">
  <criterion>BBB</criterion>
  <criterion>CCC</criterion>
  <criterion>DDD</criterion> </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document, the wanted result is produced:

<root>
    <criteria>
        <criterion>BBB</criterion>
        <criterion>CCC</criterion>
        <criterion>DDD</criterion>
    </criteria>
</root>

Do note:

  1. The use of the identity template.

  2. How the identity template is overriden by a specific template -- only for a criterion element, whose string value is 'AAA'.

like image 25
Dimitre Novatchev Avatar answered Sep 22 '22 10:09

Dimitre Novatchev