xsl-list
[Top] [All Lists]

Re: Rename attributes and nodes maybe using variables

2003-07-09 01:55:06
Hi Kai,

In the XML-File are nodes with the name "IMAGE" and the first
attribute should reflect the name and the second attribute the
location of the image. The name of these attribute is not fix, but
in the result the furst attribute should be named "name" and the
second "url" and the old name should be stored in attribute so in
the second transformation these attributes get there old name.

This requirement seems to be saying that the names of the attributes
aren't known in advance, and you want to use their "position" to
determine how to treat their content. In XML, the order of attributes
is insignificant -- XML parsers are not required to report attributes
in the same order as they encounter them. As far as an XSLT processor
is concerned:

  <IMAGE name="test5" url="/vol/pictures/test5.jpg"/>

is exactly the same as:

  <IMAGE url="/vol/pictures/test5.jpg" name="test5"/>

Of course attributes are reported in some kind of order, so you can
use @*[1] and @*[2] to get the "first" and "second" attribute if you
want, but you have no guarantees from processor to processor (or even
from transformation to transformation) about whether @*[1] is going to
give you the name attribute or the url attribute.

What this boils down to is that if you really need to use the position
of an attribute to determine its meaning, then you need to use a
non-XML-aware process to do it -- something that treats the XML
document as a string of characters. You can't use XSLT.

On the other hand, if you can guess at the names that might be used
for the attributes, then you can rename them by creating templates
that match the old name and create an attribute with the new name. For
example, to map the attribute 'label' to an attribute called 'name',
use the following template:

<xsl:template match="@label">
  <xsl:attribute name="name">
    <xsl:value-of select="." />
  </xsl:attribute>
</xsl:template>

To also add an 'old_atr_name' attribute with the value of the name of
the original attribute, use another <xsl:attribute> instruction to
create the attribute:

<xsl:template match="@label">
  <xsl:attribute name="name">
    <xsl:value-of select="." />
  </xsl:attribute>
  <xsl:attribute name="old_atr_name">
    <xsl:value-of select="name()" />
  </xsl:attribute>
</xsl:template>

You should be able to put together a similar template to map the
'locaction' attribute to a 'url' attribute.

Just add these templates to the templates from yesterday.

In other parts of the File are Nodes with the node-name REGIONSET
and the attribute "image". But in this attribute "image" I need not
the name, but the location where I can find the file.

So this attribute has to be replaced with the url of the node IMAGE
where the value of the "image" and "name" are the same. (Maybe
saving the url of the images in variables with the name of the image
should be helpful)

To do this, you need a key. Index all the <FOTO>, <PHOTO> etc.
elements by their name (or equivalent) attribute. For example:

<xsl:key name="images"
         match="FOTO | PHOTO | BILD | BILDCHEN | IMG | IMAGE"
         use="@name | @label" />

Then to get the URL of the image with a particular name (e.g.
'test1'), you can use the key to retrieve the relevant element, and
then step down to its url attribute:

  key('images', 'test1')/@url

So the template for the <REGIONSET> element is:

<xsl:template match="REGIONSET">
  <REGIONSET image="{key('images', @image)/@url}"
             oldvalue="{(_at_)image}">
    <xsl:apply-templates />
  </REGIONSET>
</xsl:template>

Again, just add this template to your stylesheet.

The second stylesheet:

The second should undo all, the first stylesheet did. So rename ever
changed node and attribute with the original stored name and value
and delete the new created attributes.

Again, you need the identity template as the basis for your
stylesheet, because mostly you just want to copy the nodes.

Then you need a template that matches an <IMAGE> element:

<xsl:template match="IMAGE">
  ...
</xsl:template>

You need to work out the names of the new element, its 'name'
attribute and its 'url' attribute. You can do this by checking if the
'oldname', 'old_atr_name' and/or 'old_atr_url' attributes are present,
and using their values if they are, or the default values ('IMAGE',
'name' and 'url') if they aren't:

  <xsl:variable name="new_ele">
    <xsl:choose>
      <xsl:when test="@oldname">
        <xsl:value-of select="@oldname" />
      </xsl:when>
      <xsl:otherwise>IMAGE</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="new_atr_name">
    <xsl:choose>
      <xsl:when test="@old_atr_name">
        <xsl:value-of select="@old_atr_name" />
      </xsl:when>
      <xsl:otherwise>name</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:variable name="new_atr_url">
    <xsl:choose>
      <xsl:when test="@old_atr_url">
        <xsl:value-of select="@old_atr_url" />
      </xsl:when>
      <xsl:otherwise>url</xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

To use these names for the new element and attributes, you need to
create them with <xsl:element>/<xsl:attribute> instructions and use an
attribute value template in the name attribute, pointing to the
relevant variable:

  <xsl:element name="{$new_ele}">
    <xsl:attribute name="{$new_atr_name}">
      <xsl:value-of select="@name" />
    </xsl:attribute>
    <xsl:attribute name="{$new_atr_url}">
      <xsl:value-of select="@url" />
    </xsl:attribute>
  </xsl:element>

The template for the <REGIONSET> element is even easier because you
just need to make the value of the image attribute the value of the
oldvalue attribute:

<xsl:template match="REGIONSET">
  <REGIONSET image="{(_at_)oldvalue}">
    <xsl:apply-templates />
  </REGIONSET>
</xsl:template>

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list