BizTalk Utilities CV ,   Jobs ,   Code library
 
Home Page


Add/Edit your code items
Search the code library
Browse for the code library


Schemas, xsd, xdr
XMLStarlet Command Line XML Toolkit
W2XML v2.0 SR1
Free DTD for Academic Publishers
Arquemie for XML Schemas
XMLStarlet Command Line XML/XSLT Toolkit
XSD Merger


 
 

<< reBloggerSEO >>


By Cristian Georgescu
First Posted 05/23/2003
Times viewed 213

Punctuated Changes In XML Documents


This post contains attachments
v20030523144852.zip 

Summary How to change the value of only one attribute in an XML document.

More precisely, we want to transform an XML document into another XML document so that that only one attribute of a specific element (or set of elements) has a different value and everything else is the same. This kind of transformation is needed in many practical situations, like updating a property in a configuration file or changing the SOAP location in a WSDL file.

Let's consider a simple example: a list of employees with some data. Suppose this is stored in the following XML file company.xml (with the DTD inlined at the begining of the document):

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

<!DOCTYPE model [
  <!ELEMENT company (company-name?, employees)>
  <!ELEMENT company-name (#PCDATA)>
  <!ELEMENT employees (employee*)>
  <!ELEMENT employee (first-name, last-name, salary)>
  <!ATTLIST employee
    ssn ID #REQUIRED
    title (developer | manager | director) developer
  >
  <!ELEMENT first-name (#PCDATA)>
  <!ELEMENT last-name (#PCDATA)>
  <!ELEMENT salary (#PCDATA)>
]>

<company>
    <company-name>Apex Industries</company-name>

    <employees>

      <employee ssn=123-45-1001 title=developer>
        <first-name>Adam</first-name>
        <last-name>Smith</last-name>
        <salary>50000</salary>
      </employee>

      <employee ssn=123-45-1002 title=developer>
        <first-name>Brad</first-name>
        <last-name>Smith</last-name>
        <salary>50200</salary>
      </employee>

      <employee ssn=123-45-1003 title=manager>
        <first-name>John</first-name>
        <last-name>Smith</last-name>
        <salary>70300</salary>
      </employee>

      <employee ssn=123-45-1004 title=director>
        <first-name>Will</first-name>
        <last-name>Smith</last-name>
        <salary>90400</salary>
      </employee>

    </employees>

</company>

The mission is to promote all employees with @title=developer to @title=manager (meaningless but fun).

First, we need to know how to perform an identity transform on the entire document. The following template does just this, by copying each node in the source to the output:

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

The previous template consists in fact of the root template / and a set of two template rules, one for matching any attribute @* and the other for matching any element *. Therefore all elements and attributes are copied to the output with no change (other then entities will be expanded and white space may be removed).

Therefore the previous template is equivalent with the following three templates:

  <!-- Root template. -->
  <xsl:template match=/>
    <xsl:text>&lf;</xsl:text>
    <xsl:text>&lf;</xsl:text>
    <xsl:apply-templates/>
  </xsl:template>

  <!-- Identity transform for elements. -->
  <xsl:template match=*>
    <xsl:copy>
      <xsl:apply-templates select=@*/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <!-- Identity transform for attributes. -->
  <xsl:template match=@*>
    <xsl:copy-of select=./>
  </xsl:template>

We need to override the identity transform for attributes with the following template that matches all elements named employee with an attribute title having the value developer and changes it to manager:

  <xsl:template match=employee/@title>
    <xsl:choose>
      <xsl:when test=.='developer'>
        <xsl:attribute name=title>manager</xsl:attribute>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select=./>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

Overriding is simply done by joining the last template to the previous list of identity templates. This works because a template with a next more specific matching pattern has a higher priority.

And we are done. Here is the complete XSLT transform, the file change.xsl:

<?xml version=1.0 ?>

<!-- begin stylesheet -->
<xsl:stylesheet
  version=1.0
  xmlns:xsl=http://www.w3.org/1999/XSL/Transform
>

  <!-- stylesheet output -->
  <xsl:output method=xml encoding=UTF-8/>

  <!-- Root template. -->
  <xsl:template match=/>
    <xsl:apply-templates/>
  </xsl:template>

  <!-- Identity copy of elements. -->
  <xsl:template match=*>
    <xsl:copy>
      <xsl:apply-templates select=@*/>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <!-- Identity copy of attributes. -->
  <xsl:template match=@*>
    <xsl:copy-of select=./>
  </xsl:template>

  <!-- Change specific attribute. -->
  <xsl:template match=employee/@title>
    <xsl:choose>
      <xsl:when test=.='developer'>
        <xsl:attribute name=title>manager</xsl:attribute>
      </xsl:when>
      <xsl:otherwise>
        <xsl:copy-of select=./>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

<!-- end stylesheet -->
</xsl:stylesheet>

We can now call the transform (with the Microsoft XSLT processor) from the command line:

C:\>msxsl company.xml change.xsl -o company2.xml

The resulting XML file company2.xml will be:

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

<company>
    <company-name>Apex Industries</company-name>

    <employees>

      <employee ssn=123-45-1001 title=manager>
        <first-name>Adam</first-name>
        <last-name>Smith</last-name>
        <salary>50000</salary>
      </employee>

      <employee ssn=123-45-1002 title=manager>
        <first-name>Brian</first-name>
        <last-name>Smith</last-name>
        <salary>50200</salary>
      </employee>

      <employee ssn=123-45-1003 title=manager>
        <first-name>John</first-name>
        <last-name>Smith</last-name>
        <salary>90300</salary>
      </employee>

      <employee ssn=123-45-1004 title=director>
        <first-name>Will</first-name>
        <last-name>Smith</last-name>
        <salary>180400</salary>
      </employee>

    </employees>

</company>

That's all. We have now a company with all managers and no developers. Of course all names are fictional and any similarity between the above example and actual things is totally conincidental (unless of course, it isn't).

Additional information

Further additional information


Rate this article on a scale of 1 to 10 (0 votes, average 0)

Your vote :  

<< reBloggerSEO >>





Leave a comment for this article
Your name
Your email (optional)
Your comment
Optional: Upload an attachment
Enter the code shown:

 
 

    Email TopXML