sobota, 12 stycznia 2013

XSLT Identity Transformation Overriding pattern and xsltproc - easy xml files manipulation (unix only)

Recently I faced the problem where I had to modify bunch of XML files scattered all over the file system. The modification was very small - move value of one XML tag into another, and remove the original tag.

The problem itself seems very simple, so writing a piece of software for that seems to be like killing a sparow with a bazooka.

Here is what I came up with

XSLT Identity Transformation Overriding pattern

Because our modification is very small, and we want to preserve most of our original document structure, we should start with XSLT Identity Transformation which looks like following

 
 
  
   
  
 

Now let's say, that we want to add another fridge to our kitchen in the following xml document, but only if the house is locked (house/isLocked element is set to true)

<house>
 <isLocked>true</isLocked>
 <kitchen>
  <cupboard>
   <capacity>8</capacity>
  </cupboard>
  <fridge>
   <boughtOn>2013-01-10T20:45:45.033+01:00</boughtOn>
   <innerTemperature>-4</innerTemperature>
  </fridge>
  <isLocked>true</isLocked>
 </kitchen>
 <livingRoom>
  <isLocked>true</isLocked>
 </livingRoom>
</house>

We can modify (override) our XLST Identity Transformation in following way:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

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

 <!-- add a node fridge into our kitchen -->
 <xsl:template match="kitchen">
  <xsl:copy>
   <xsl:apply-templates/>
   <!-- only if house is locked -->
   <xsl:if test="/house/isLocked = 'true'">
   <fridge>
    <boughtOn>2013-01-10T20:45:45.033+01:00</boughtOn>
    <innerTemperature>-10</innerTemperature>
   </fridge>
   <!-- add new line -->
   <xsl:text>
   </xsl:text>
   </xsl:if>
  </xsl:copy>
 </xsl:template>    
 <!-- "unlock" the house (remove isLocked element) -->
 <xsl:template match="/house/isLocked">
 </xsl:template>
</xsl:stylesheet>

You can quickly see the result of transformation using xsltproc, which is a little unix commandline program for performing xslt transformations.

xsltproc newFridge.xslt kitchen.xml > kitchen.xml.out

Now, if you want to apply the same tranformation to a bunch of files selected from system with, say, find method, you can perform following

for f in `find -name "house*.xml"` ; do xsltproc xslt/newFridge.xslt "$f" > "$f.out"; done

If you don't care for the *.out files, you can always add mv so it will look like transformation was performed on on the original files:

for f in `find -name "house*.xml"` ; do xsltproc xslt/newFridge.xslt "$f" > "$f.out" ; mv "$f.out" "$f" ; done

These altoghether will give you a very easy, yet powerful, tool for mass xml manipulation. I run all of these commands on cygwin

Brak komentarzy:

Prześlij komentarz