XSLT attributes, access

Attributes

1. How to get the name of an attribute
2. Attribute value normalisation
3. How to make an element value a Mailto reference
4. Adding attributes
5. Attribute TRUE or FALSE
6. How to convert elements to attributes
7. How can I keep the attribute order
8. Attribute nodes are not children of element nodes
9. How to optionally create an attribute
10. How to generate double quotes inside attribute string
11. Using logical OR with multiple attribute values
12. How to select an attribute
13. How to get the contents of the attlist
14. How to Serialise element content
15. How can I escape data within an xsl:attribute declaration
16. How to generate a drop down box in the HTML
17. Copy attribute from a node to another
18. How to set attributes in the xsl by taking attributes values from xml doc
19. How to search for individual characters within an attribute
20. Understanding Attribute selection
21. Attribute selection
22. How to use Attribute sets
23. Implied Attributes
24. Attribute defaulting
25. How to detect whether an attribute doesnt have a value
26. REQUIRED vs IMPLIED attributes
27. Selecting unique value of an attribute
28. Angle bracket nested in an attribute
29. Matching attributes that contains quotes
30. select ALL attributes except a specific one
31. how to show 0.00 when no attribute is present
32. Dynamic testing of an attribute
33. Identifying the element associated with attribute
34. Copying Groups of Attributes
35. How to select only the attributes present in the source document
36. xmlns attribute problems
37. Attributes, which namespace do they take up?
38. Is the current node an attribute?
39. Conditionally adding attributes - or default
40. Attribute selection in the child axis

1.

How to get the name of an attribute

..

 <xsl:for-each select="@*"> 
    <xsl:text>Value of </xsl:text>
           <xsl:value-of select="name(.)"/>
    <xsl:text> is </xsl:text><xsl:value-of select="."/>
 </xsl:for-each>

unless I mistake

            

2.

Attribute value normalisation

David Carlisle

attribute value normalisation goes through a sequence of slightly bizarre steps which I didn't want to go into so simplified greatly but the end result most of the time is that newlines in attribute values get reported as spaces, this is why, in XSLT if you go

<xsl:attribute name="x">
1
2
3
</xsl:attribute>
it will get output as
  x="&#10;1&#10;2&#10;3&#10;"

This attribute, because the newlines are quoted has newlines in its value, but

x="
1
2
3
"

which you might expect to be the result of the xsl:attribute would not work as the xml parse will report those as spaces not newlines.

3.

How to make an element value a Mailto reference

Ben Robb

 Use the xsl:attribute call in your style sheet 
and then treat it like a normal <A href=...> tag.
 
<p>
<xsl:attribute name="href">mailto:<xsl:value-of select="email"/>
</xsl:attribute>
    Mail us!
</p>

or if you want the email address to be printed on the screen as well:

    <xsl:attribute name="href">mailto:<xsl:value-of
select="email"/></xsl:attribute>:<xsl:value-of select="email"/>

or from Steve Muench

<A href="mailto:{email}"><xsl:value-of select="email"/>
            

4.

Adding attributes

David Carlisle

Q: expansion
I'm looking for a way to match all elements <x> (which may have
 attributes) and add new attributes:
   
     <x>                 --> <x             b="b" c="c">
     <x a="a">           --> <x a="a"       b="b" c="c">
     <x a="a" d="d">     --> <x a="a" d="d" b="b" c="c">

Answer:


<xsl:template match="x">
<x b="b" c="c">
<xsl:copy-of select="@*"/>
</x>
</xsl:template>

            

5.

Attribute TRUE or FALSE

Michael Kay

Q expansion: I want something to happen when a certain attribute is set to true (and something to happen when it is set to false).

I assume you mean, you want something to happen when it is set to "true" (since the value of an attribute is a string, not a boolean).

Then write:

<xsl:if test="@att='true'">

6.

How to convert elements to attributes

David Carlisle

e.g.

<atom  phase="gas">
   <name>Hydrogen</name>
   <symbol>H</symbol>
   <boiling_point units="Kelvin">20.28</boiling_point>
</atom>

the result one:
<atom phase="gas" name="Hydrogen" symbol="H">
    <boiling_point units="Kelvin">20.28</boiling_point>
</atom>
    

The first select picks up attributes and elements that don't have element children or attributes, and makes attributes of them. The second select picks up elements with element children or attributes, and text nodes.

as written, comments, pis etc get thrown away, but could be added to the second select.

<xsl:stylesheet 
  xmlns:xsl="http://www.w3.org/1999/XSL/Tranform"     version="1.0"
  default-space="strip">


<xsl:template match="*">
<xsl:copy>
<xsl:for-each select="@*|*[not(* or @*)]">
<xsl:attribute name="{name(.)}"><xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates select="*[* or @*]|text()"/>
</xsl:copy>
</xsl:template>


</xsl:stylesheet>

7.

How can I keep the attribute order

Steve Schafer


>if the order is important, how can I keep the attribute order instead
>of using @*?

Attributes are intrinsically unordered. If you are doing some processing that relies on the attributes being in some particular order, you are going to have to rethink what you're doing.

8.

Attribute nodes are not children of element nodes

James Clark

This was discussed at enormous length in the XSL WG. It boiled down to a choice between the design as it is now and an alternative design in which

1. "parent" is the inverse of "children"

2. there's a new "bearer" axis that gets from an attribute/namespace to the element that bears it

3. there's a new "parent-or-bearer" axis that means what parent means now

4. .. is an abbreviation for parent-or-bearer::node()

The WG decided to stick with the current design because

a) the alternate design added significant complexity (two new axes) without any increased functionality

b) it complicates usage of ancestor. Either

ancestor on an attribute is always empty, or
you introduce new axes that are like ancestor/ancestor-or-self but work for attributes, or
you make ancestor no longer equivalent to parent or parent's parent etc

None of these seem attractive options.

9.

How to optionally create an attribute

James Clark


<element xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xsl:version="1.0">
 
<element>
 <xsl:if test="true()">
  <xsl:attribute name="name">value</xsl:attribute>
 </xsl:if>
 foobar
</element>

            

10.

How to generate double quotes inside attribute string

Warren Hedley

I'll assume that single quotes don't work. It would actually be nice if XSLT processors occasionally output attributes surrounded by single quotes, as this would solve your problem. What is your output method incidentally - if it is XML or HTML, the quotes are the least of your worries - the '<' at the start of the attribute is much harder to deal with.

Anyway, to produce dubious output, you have to resort to dubious methods ...

As of saxon 5.4, there is an extension attribute that disables output escaping in attribute values, allowing you to create the kind of invalid output you're looking for.

The following stylesheet

<?xml version="1.0" encoding="iso-8859-1"?>

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

<xsl:template match="/">
  <input type="text">
    <xsl:attribute name="value" saxon:disable-output-escaping="yes"
        xmlns:saxon="http://icl.com/saxon">
      <xsl:text>&lt;?php echo global_get("vg_answer.qa[1]" ); ?></xsl:text>
    </xsl:attribute>
  </input>
</xsl:template>

</xsl:stylesheet>

produces

<?xml version="1.0" encoding="utf-8"?>
<input type="text" value="<?php echo global_get("vg_answer.qa[1]" ); ?>"/>

11.

Using logical OR with multiple attribute values

Mike Brown



> I would like to output "Smith" if the "name" attribute is 
> "John" or "Joe".

Vertical bar is not a logical OR in XSLT and XPath. It's either a vertical bar character, or in some cases it is a union operator for combining node-sets. For what you want to do, just use the word "or" between complete expressions.

<xsl:if test="@name='John' or @name='Joe'">
  <xsl:text>Smith</xsl:text>
</xsl:if>

            

12.

How to select an attribute

Linda van den Brink

Q: Expansion
for an element,
<SLIDE 
REF="d://programs//xml//astron//documents//Hist_1.xml">History</SLIDE>


<xsl:template match="SLIDE">
     <xsl:text> text1 </xsl:text>
     <xsl:value-of select="."/>
     <xsl:text> text2 </xsl:text>
</xsl:template>

The output of this is: text1 History text2

So, why do I get element content, where I want to have the attribute? How do I correctly select the contents of the REF attribute?

This is a bit confusing, I know. You are not selecting the value of the REF attribute of SLIDE, but the value of SLIDE element (that happens to have a REF attribute). To get the attribute value itself, use:


<xsl:template  match="SLIDE/@REF"/> to match on the element which has a 'REF' attribute, or

<xsl:value-of select = "@REF" to match on 
  the attribute  within the SLIDE template

13.

How to get the contents of the attlist

Richard Lander


DATE[@FIND_VALUE] means select DATE elements that have a
FIND_VALUE attribute.


You want: DATE/@FIND_VALUE. That selects the FIND_VALUE
attribute of the DATE element. See the difference?

XPATH information within [ and ] is provided for context,
not selection.


            

14.

How to Serialise element content

David Carlisle


Q expansion: How do I get

<housenumber>111</housenumber>
<streetname>Main</streetname>
<streetsuffix>St.</streetsuffix>

To come out as a hidden address attribute of element input
with spaces between?


<input type="hidden" name="Address" 
   VALUE="{/housenumber} {/Streetname} {/streetsuffix}"/>

            

15.

How can I escape data within an xsl:attribute declaration

David Carlisle


Q expansion

I tried 
<xsl:text>
       <td valign="bottom" align="right" nowrap>
        <font face="Arial" size="-1" color="Silver">
         <b>&nbsp;&nbsp;Welcome, Brett</b>
        </font>
       </td>
      </xsl:text>
     
	 
	 I want the <td valign=....> through the </td> to be left alone; my parser
	 keeps trying to use them as XML elements and gets upset at me... this
	 happens with or without <xsl-text>.  How can I let the element know I am not
	 speaking XSL to it?
	 

Parsers don't get upset they just report errors (honestly:-) You have put elements inside xsl:text, but the content of xsl:text should be, well, text. The parser is using them as XML because they are XML. literal result elements go straight into the template, not inside xsl:text. Also the &nbsp; entity will generate an undefined entity unless you have defined it in the stylesheet. Use &#160;.

XML attributes always have to have a value, so you can't use `nowrap' without giving it a value.

Mike Brown adds

To expand on what David Carlisle said, even if you did manage to get the text in there (and there *is* a way to do it), you'd be disappointed with the results.

Once you strip "&lt;", ">", and "&" of their special meaning as element and entity reference boundaries, they will forever be just those individual characters. The consequences of this is are that they cannot be represented in a valid XML or HTML document as anything other than entity references like &lt; &gt; and &amp;.

There is actually a way to have two wrongs make a right by disabling that output escaping on a case-by-case basis, but you're really using the wrong approach if you are trying to treat markup as text. Let that structured information have some dignity.

Try this instead of the <xsl:text>...</xsl:text>:

<xsl:variable name="MyResultTreeFragment">
  <td valign="bottom" align="right" nowrap="nowrap">
    <font face="Arial" size="-1" color="Silver">
      <b>Welcome, Brett</b>
    </font>
  </td>
</xsl:variable>
<xsl:copy-of select="$MyResultTreeFragment"/>

            

16.

How to generate a drop down box in the HTML

Ben Robb


- --- XML ---

<item>1</item>
<item>2</item>
<item>3</item>
.....
<item>10</item>


- --- XSL ---

<select name="number">
<xsl:for-each select="item">
<option value="{text()}"><xsl:value-of select="Whatever" /></option>
</xsl:for-each>
</select>

That is probably the most elegant way - you never need to actually know how many options you have, so you can extend it by only changing the XML in one place.

17.

Copy attribute from a node to another

David Carlisle


From <node align="right">some text</node>
I need
  <node align="right">some text
     <subnode>
       <format width="45" align="right"/>
     </subnode>
   </node>
All data in 'subnode' and 'format' are static except for 'align'
 that comes from 'node'. 


<xsl:template match="node">
<xsl:copy>
  <xsl:copy-of select="@*"/>
  <xsl:apply-templates/>
<subnode>
  <format width="45" align="{@align}"/>
</subnode>
</xsl:copy>
</xsl:template>

            

18.

How to set attributes in the xsl by taking attributes values from xml doc

David Carlisle


<INPUT>
       <xsl:attribute name="Name">
               <xsl:value-of select="Name"/>
          </xsl:attribute>

that takes the value of the node selected by "Name" which would be a _child element_ of the current node.

So you don't want that.

You could do

<INPUT>
       <xsl:attribute name="Name">
               <xsl:value-of select="@Name"/>
          </xsl:attribute>

which would work or not, depending on what you want to happen if the source element does not have a Name attribute. The above would always produce a Name attribute, and would give Name="" if the input did not supply an attribute. If that is what you want the above is OK, but equivalent to the simpler

<INPUT Name="{@Name}>

If you don't want the input to have a Name attribute if the source does not have one, you can do

<INPUT>
 <xsl:copy-of select="@Name"/>

If you want all attributes copied you can add more such lines or do

<INPUT>
 <xsl:copy-of select="@*/>

19.

How to search for individual characters within an attribute

David Carlisle

<xsl:if test="contains(@CLAS,'X')">

            

20.

Understanding Attribute selection

Steve Muench

Given an XML source of:

<foo attr="bar"/>

A stylesheet of:

<xsl:stylesheet xmlns:xsl=
"http://www.w3.org/1999/XSL/Transform" version="1.0">
 <xsl:template match="foo">
   <xsl:variable name="varValue0" select="bar"/>
   <xsl:variable name="varValue1" select="'bar'"/>
   <xsl:variable name="varValue2">bar</xsl:variable>
   <xsl:if test="@attr = $varValue0">
     attr = varvalue0
   </xsl:if>
   <xsl:if test="@attr = $varValue1">
     attr = varvalue1
   </xsl:if>
   <xsl:if test="@attr = $varValue2">
     attr = varvalue2
   </xsl:if>
 </xsl:template>
</xsl:stylesheet>

Produces

     attr = varvalue1
     attr = varvalue2

NOTE

(1) attr does *NOT* match $varValue0 since the variable was set equal to an empty nodeset when select="bar" did not find any children elements named "bar".
(2) There are two ways to get a string value assigned to a variable:
 <!-- Note the single quotes around 'string' -->
  <xsl:variable name="x" select="'string'"/>
  <xsl:variable name="y">string</xsl:variable>

21.

Attribute selection

Mike Kay

> I wish to go through each file and copy those nodes to the
> result tree that EITHER have no "lang" attribute set,
> or that DO NOT have lang="german" or lang="french".

 Try:

<xsl:copy-of select="selection[not(@lang) or 
not(@lang='german' or @lang='french')]">

Always remember that a!=b in XSLT does not mean the same as
not(a=b). If @a doesn't exist, then @a=b and @a!=b are both
false.

22.

How to use Attribute sets

G. Ken Holman



<?xml version="1.0"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nl "
">
]>
<xsl:stylesheet 
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:fo="http://www.w3.org/1999/XSL/Format"
                 version="1.0">

<xsl:param name='hd-size' select="10"/>

<xsl:attribute-set name="font-metrics">
   <xsl:attribute name="font-size">
     <xsl:value-of select="$hd-size"/>pt</xsl:attribute>
   <xsl:attribute name="space-before.minimum">
     <xsl:value-of 
   select="round(0.8 * $hd-size)"/>pt</xsl:attribute>
   <xsl:attribute name="space-before.maximum">
     <xsl:value-of 
       select="round(1.2 * $hd-size)"/>pt</xsl:attribute>
   <xsl:attribute name="space-before.optimum">
     <xsl:value-of select="$hd-size"/>pt</xsl:attribute>
</xsl:attribute-set>

<xsl:template match="item">
   <fo:block xsl:use-attribute-sets="font-metrics">
     <xsl:apply-templates/>
   </fo:block>
</xsl:template>

</xsl:stylesheet>

23.

Implied Attributes

David Carlisle

People often go in a template

<table border="{@border}">

hoping to copy the border attribute from some input table to an output table but that is only ok if the input attribute is always there, otherwise you get border="". If you want to supply a default

<table  border="1001" >
  <xsl:copy-of select="@border"/>

makes an element node with name table and one attribute node with name border

<xsl:copy-of select="@border"/>

either does nothing, in which case you get what you have above or it generates an attribute node with name border. XSLT specifies that if you add two attribute nodes of the same name to an element the first one is discarded. So in this case the original attribute node with value 1001 is replaced by the border attribute copy-of'ed from the source tree.

So effectively you get a copy of the original except that you get a default value of 1001 in the case when there was no border attribute originally.

24.

Attribute defaulting

David Carlisle

It is always possible to do the attribute defaulting in the stylesheet so that it works even if the dtd or schema isn't read.

So if you want

<xxx>

to act the same as

<xxx yyy="yes">
    

but are not sure if the dtd or schema supplying that default will be acted upon, then write your xsl like so:

<xsl:template match="xxx">
 <xsl:variable name="yyy">
  <xsl:value-of select="@yyy"/>
  <xsl:if test="not(@yyy)">yes</xsl:if>
 <xsl:variable>
...
... rest of template, but use $yyy instead of @yyy ....

25.

How to detect whether an attribute doesnt have a value

Mike Kay

To test whether attribute id exists: <xsl:if test="@id">

To test whether attribute id exists and is not zero-length: <xsl:if test="string(@id)">

26.

REQUIRED vs IMPLIED attributes

Nikolai Grigoriev

In such cases, it may be convenient to delegate attribute processing to separate templates, instead of a for-each loop. This is more verbose, but (IMHO) it yields a better manageable code:

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

<!-- A default rule for processing link attributes -->
<xsl:template match="Link/@*" priority="-1">
    <xsl:copy/>
</xsl:template>

<!-- To change a name of an attribute -->
<xsl:template match="Link/@linkid">
    <xsl:attribute name="name">
        <xsl:value-of select="."/>
    </xsl:attribute>
</xsl:template>

<!-- To prevent an attribute from being copied -->
<xsl:template match="Link/@unused-attribute"/>

27.

Selecting unique value of an attribute

Paul Terray

The problem is the one of doing an index from entries in the text. You want a list where word do not repeat themselves. The example here use empty tags with entry as attributes, but you can do with the same algorithms if you use enclosing tags.

First, My  XML snippet :
<text>
<index entry="thing"/> blablabla <index entry="stuff"/> blabla <index 
entry="this"/>
bliblabla<index entry="thing"/> bla bli bla<index entry="this"/> bli <index 
entry="stuff"/>
<index entry="thing"/>and bla and bli <index entry="stuff"/>
</text>

I want to get a list with :
- -stuff
- -thing
- -this

There are two ways of doing it :

Compare with the preceding element in a sorted subtree, 
with a sub-template. This give a solution 


   <xsl:template match="text">
     <xsl:apply-templates select="index">
         <xsl:sort select="@entry"/>
     </xsl:apply-templates>
   </xsl:template>


   <xsl:template match="index">
     <xsl:if test="not(@entry = preceding::index/@entry)">
       <xsl:value-of select="@entry"/>
     </xsl:if>
   </xsl:template>

This solution is clean to read, although I do prefer the other one for compactness and performance.

Oliver Becker offers

The other solution is more complicated to understand. By using a key, you group all identical entries, and take only the first of them, like this :

Something like this: Define a key for every @entry of index:

<xsl:key name="paul" match="index" use="@entry" />

Then walk through your index elements and choose only the first of each group (i.e. each key)

<xsl:for-each 
select="index[generate-id()=generate-id(key('paul',@entry)[1])]">

now you have unique entries which need to be sorted:

<xsl:sort select="@entry" />

Ok - here you are! Output, and that's all:

<xsl:value-of select="@entry" />

The complete template is

<xsl:template match="paul">
    <xsl:for-each
         select="index[generate-id()=generate-id(key('paul',@entry)[1])]">
       <xsl:sort select="@entry" />
       <xsl:value-of select="@entry" />
       <xsl:text>&#xA;</xsl:text>
    </xsl:for-each>
</xsl:template>

I like that one, because it is rather subtle, and allows me to do a multiple level index easily.

Another advantage is how easy it is to make hypertext entry beside (you need one per element in the xml).

However, it is a question of taste and I don't see how one is better than the other.

28.

Angle bracket nested in an attribute

Steve Muench

| so, in my XSL file, I want to use :
| <a HREF="http://<xsl:value-of select="$server">">

You'll want to use the shortcut "attribute value template" syntax that allows you to insert the value of an XPath expression in the middle of a literal attribute's value by surrounding the expression with curly-brackets:

  <a href="http://{$server}"/>

This is equivalent to the longer-hand version:

  <a>
    <xsl:attribute name="href">
      <xsl:value-of select="$server"/>
    </xsl:attribute>
  </a>

and is a lot less typing.

29.

Matching attributes that contains quotes

David Carlisle

The xpath states in section 1: To avoid a quotation mark in an expression being interpreted by the XML processor as terminating the attribute value the quotation mark can be entered as a character reference (&quot; or &apos;). Alternatively, the expression can use single quotation marks if the XML attribute is delimited with double quotation marks or vice-versa.

From what I understand, &apos; and &quot; get expanded by the xml parser before the processor ever sees them, so they can't be used to solve this particular problem.

Ah, good catch.

You can't have a single string literal in xpath that contains both a " and a ' but you can do

match="xxx[.=concat('&quot;',&quot;'&quot;)]"

which, after XML entity expansion gives a value of

xxx[.=concat('"',"'")]

for the match expression which tests the value of the element against the value of the concat function, which is the string "'

I have no idea if that causes XSL implementations to repeatedly concatenate the strings.

Paul_Dick adds the example.

David writes: >but you can do match="xxx[.=concat('&quot;',&quot;'&quot;)]"

Yes, the working template is a bear to decipher, but it does work.

- ---XML Source ---
<channel>
  <location name=' "x"  &apos;a z'/>
</channel>

- ---XSLT ---
<xsl:template 
match="channel/location[@name=concat(' &quot;x&quot; ',&quot;
&apos;a z&quot;)]">
  <xsl:value-of select="@name"/>
</xsl:template>

30.

select ALL attributes except a specific one

Jeni Tennison


> <xsl:copy-of select="@*" />
> will copy all the attributes of a tag.
>
> <xsl:copy-of select="@type|@language" />
> will copy only the 'type' and 'language' attributes.
>
> How do I get a statement that will select ALL the attributes except a
> specific one?
	

If the attribute that you want to weed out is unprefixed (i.e. it's in the null namespace) then you can use:

  <xsl:copy-of select="@*[local-name() != 'type']" />

to copy all but the 'type' attribute. It selects all attributes (@*) and then filters out all those whose local name equals 'type'.

If the attribute has a prefix (i.e. it's in a namespace) then you should use:

  <xsl:copy-of select="@*[count(.|../@xml:lang) !=
                          count(../@xml:lang)]" />

to copy all but the 'xml:lang' attribute. Alternatively, you can use:

  <xsl:copy-of select="@*[generate-id() !=
                          generate-id(../@xml:lang)]" />

which does exactly the same thing. It selects all attributes and then filters out those that *are* the xml:lang attribute. Or, if you want to, you can use:

  <xsl:copy-of select="@*[not(local-name() = 'lang' and
                              namespace-uri() =
                                 'http://www.w3.org/XML/1998/namespace')]" />

This selects all attributes and filters out those whose local name is lang in the XML namespace.

31.

how to show 0.00 when no attribute is present

Gary L Peskin



> how can I apply the template which tests the @val1,@val2,@val3 attributes
> and applies the appropriate formatting, if I don't have those elements
> present in the xml??

Here's the solution I came up with. The PageData template is always executed and provides the outer HTML. If there are no rows, the xsl:otherwise emits the 0.00 field values. Otherwise, we apply-templates to the attributes as before.

You don't need to replicate the built-in templates that XSLT automatically provides so I removed them.


<xsl:stylesheet version="1.0"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"    xmlns:z="#RowsetSchema"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="PageData">
  <HTML>
  <HEAD/>
  <BODY>
  List of Values
  <br/>
  <xsl:choose>
    <xsl:when test="//z:row">
      <xsl:apply-templates select="//z:row"/>
    </xsl:when>
    <xsl:otherwise>
      0.00<br/>0.00<br/>0.00
    </xsl:otherwise>
  </xsl:choose>
  <br/>
  </BODY>
  </HTML>
</xsl:template>
	
<xsl:template match="z:row">
  <xsl:apply-templates select="@val1"/>
  <br/>
  <xsl:apply-templates select="@val2"/>
  <br/>
  <xsl:apply-templates select="@val3"/>
</xsl:template>
	
<xsl:template match="@*">
  <xsl:choose>
    <xsl:when test=".&gt;0">
      <Font color="green">
        <xsl:value-of select="format-number(.,'##.##')"/>
      </Font>
    </xsl:when>
    <xsl:when test=".&lt;0">
      <Font color="red">
        <xsl:value-of select="format-number(.,'##.##')"/>
      </Font>
    </xsl:when>
    <xsl:otherwise>
      0.00
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

32.

Dynamic testing of an attribute

Jeni Tennison


><xsl:if test="./@*[name()=$attname]">    //does not work!
	

You can't say *how* it doesn't work, but it's probably to do with using the shorthand . in a test, which is not allowed. The intent of your test is:

  test="@*[name() = $attname]"

so try that instead.

>	<attribute name="width">
>		<xsl:value-of select="???"/>   
> how to specify the value of the attribute?
>	</attribute>

Presumably you want the value of the $attname attribute? The value of an attribute is given as its string value; the xsl:value-of instruction gives the string value of whatever XPath is specified within the select expression. So if you want the value of an attribute within the xsl:value-of, then use an XPath that points to that attribute:

  <xsl:value-of select="@*[name() = $attname]" />

If this is what you wanted, then to prevent the same attribute being searched for twice (once in the xsl:if test, and once in the xsl:value-of select), then you could assign the attribute to a variable and query that:

<xsl:variable name="att" select="@*[name() = $attname]" />
<xsl:if test="$att">
  <foo>
    <attribute name="width"><xsl:value-of 
	  select="$att" /></attribute>
  </foo>
</xsl:if>

A couple of things to note here. One is that the xsl:if tests whether the attribute is *present* rather than whether the attribute has a value. In other words, if $attname is 'bar', then:

  <foo bar="" />

would have the test return true. If you want to test whether the attribute is present *and* has a value, then use:

  test="string($att)"

instead.

The second thing is that I just wanted to check that you really wanted to create output that looked like:

  <foo>
    <attribute name="width">value</attribute>
  </foo>

or whether you were actually after:

  <foo width="value" />

If the latter, then you need to prefix your 'attribute' elements with xsl: to put them in the XSLT namespace and have the XSLT processor recognise them as instructions. Alternatively, you can use an attribute value template to achieve the same effect:

  <foo width="{$att}" />

33.

Identifying the element associated with attribute

Jeni Tennison


> When you have a template that matches element nodes, it's easy to
> specify choices based on particular attributes: but how do you do it
> the other way around? That is, you have a template for an attribute
> and you want to specify a choice based on the name of the element in
> which the attribute occurs (ie. test="IF THE NAME OF YOUR ASSOCIATED
> ELEMENT IS 'FOO'"). I've tried numerous permutations involving
> name() and node(), but nothing has worked so far. Please, what's the
> magic expression?
The short answer is, use:

  parent::FOO

The long answer follows:

To go from an attribute to its element involves using the parent:: axis. So, if the context node is an attribute (as it is in a template that matches attribute nodes) then you can identify the element that attribute is on with the expression:

  parent::*

(the element that is the parent of this node) or:

  parent::node()

(the *node* [which actually must be an element in this context] that is the parent of this node) or the abbreviation of the above:

To get the name of the parent, you can use the name() function, taking one of the above expressions as an argument:

  name(..)

So, to test whether the name of the parent element is 'FOO' then you could use:

  name(..) = 'FOO'

This answer will work perfectly well in most cases, but a better solution becomes apparent if you turn around the phrasing of what you're after. You want to know if this attribute has a 'FOO' element as a parent: is there are parent of this attribute that is a 'FOO' element? The expression to get to such a parent is:

  parent::FOO

(the 'FOO' element that is the parent of this node). If the parent element is a 'FOO' element, then that node will be returned. If the parent element is *not* a 'FOO' element, then no node will be returned as there is no parent FOO element. Within a test expression, if a node is returned the test returns true, if no node is returned the test returns false. So (in most situations) the following are equivalent:

  parent::FOO   is equivalent to   name(..) = 'FOO'

The situation where they are not equivalent is where namespaces are involved. The name() of a node gives the exact name for the node within the XML source. Look at the following XML:

  <foo:FOO xmlns:foo="http://www.foo.com" />

In this, the 'FOO' element is in the 'http://www.foo.com' namespace. The name() of that element is:

  foo:FOO

So, if you are using name() to test the identity of the element, then you need to use:

  name(..) = 'foo:FOO'

However, it might be that in another document (or even the same document!) you have an element like:

  <bar:FOO xmlns:bar="http://www.foo.com" />

The 'FOO' element here is in the same namespace (http://www.foo.com) but has a different prefix. Its name is:

  bar:FOO

and it would have to be tested with:

  name(..) = 'bar:FOO'

despite the fact that actually the two FOO elements in the two documents are meant to be precisely the same.

Fortunately, when you use 'pa rent::FOO' instead, it takes into account the fact that the prefix of a namespace isn't important - it's the *URI* that you have to look at. If within your XSLT you have declared the 'foo' prefix to be associated with the 'http://www.foo.com' URI using:

  xmlns:baz="http://www.foo.com"

then the XPath:

  parent::baz:FOO

will match both the 'foo:FOO' and the 'bar:FOO' elements - it looks for the equivalence in the namespace *URI* rather than the namespace *prefix*.

For this reason, it is worth getting into the habit of testing for nodes called a particular name by testing for whether the node called that name exists rather than testing whether the node is called that name.

34.

Copying Groups of Attributes

Jeni Tennison

There are a couple of ways:

You could use XML entities to give the attribute names. Define the entities in the document type declaration for the stylesheet:

<![DOCTYPE xsl:stylesheet [
<!ENTITY coreattrs '@id|@class|@style|@title'>
]>

and then use the entity name within the xsl:copy-of in the main code:

<xsl:template match="p">
   <p>
      <xsl:copy-of select="&coreattrs;" />
   </p>
</xsl:template>

Or you could store the relevant attribute names in a separate XML structure somewhere, e.g.:

- --- definitions.xml ---
<attgroup name="coreattrs">
   <attribute name="id" />
   <attribute name="class" />
   <attribute name="style" />
   <attribute name="title" />
</attgroup>
- ---

[Note: you can make up your own XML structure for the above - you might notice that it looks similar to various schema structures, and want to use a one of the schema vocabularies to represent it.]

With the above structure, you could retrieve the list of coreattrs through something like:

  document('definitions.xml')//attgroup[@name = 'coreattrs']/attribute

[Note: you could probably do this more efficiently with keys or ids.]

Perhaps store that in a variable:

<xsl:variable name="coreattrs"
              select=document('definitions.xml')//attgroup[@name 
	  = 'coreattrs']/attribute/@name" />

And then copy all those attributes whose name is equal to one of the @names of the attribute elements stored in the $coreattrs variable:

<xsl:template match="p">
   <p>
      <xsl:copy-of select="@*[name() = $coreattrs/@name]" />
   </p>
</xsl:template>

This is quite a nice method because it separates the information that you want from how you use it. You can easily go and edit definitions.xml if the membership of coreattrs changes, without having to touch the XSLT code.

A final point: xsl:copy-of gives an exact copy of a node, including its name and value, and obviously it only copies a node if it's there to be copied, so you don't need the xsl:ifs testing for its presence. If you are ever changing the name of one of these attributes - changing lang to xml:lang, for example - then you can't use xsl:copy-of. Using a separate named or moded template as David suggested gives you a lot more flexibility and extensibility than the suggestions given above, and could be combined with the second technique above quite elegantly.

35.

How to select only the attributes present in the source document

Goetz Bock


> How can I specify that <xsl:copy-of select="@*" /> should NOT select and
> copy default attributes (from the DTD) into the result document?
> 
> I want only the attributes that are present in the instance (source
> document).

DTD processing is done by the XML parser, there is no way in XSL-T to influence the parsing of the source document.

If your XML parser is a validating one, it has to add all default attribute values to elements in the source document. There is no way for XSL-T to determine wether a given attribute comes from the source or the DTD.

The only thing you could do is to test wether the value of an attrbute matches the default from the DTD (you can not parse the DTD, you've to get it from the DTD and add it to your XSL-T manually) and filter it out. (This is a lot of hand work/writing).

36.

xmlns attribute problems

Jeni Tennison



> 1. Is xmlns treated different from a normal attribute??

Yes. 'xmlns' attributes don't count as attributes as far as the XSLT processor is concerned (either on input or output). The XSLT processor automatically adds namespace declarations to the output that you create based on the namespaces of and the namespace nodes on the elements in the result tree.


> 2. In my case how do I output the value of xmlns??

Well, the simplest way is to make sure that your all-centres element in the result is in the namespace 'http://www.nda-centres.com/namespaces'. The simplest way of doing that is to include a namespace declaration on the literal result element. For example:

<xsl:template match="/">
  <all-centres xmlns="http://www.nda-centres.com/namespaces" />
</xsl:template>

But more usually you should declare the namespace on the xsl:stylesheet element as the default namespace for the stylesheet:

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns="http://www.nda-centres.com/namespaces">

<xsl:template match="/">
  <all-centres />
</xsl:template>

</xsl:stylesheet>

> 3. How will someone benifit from using a namespace declaration??

That's a hard question to answer. The namespace of an element is an essential part of an element, so it's like asking "How will someone benefit from naming an element?" They'll benefit because the element will be recognised properly by applications that are built to process elements in that namespace. The point of namespaces in general is to enable you to have documents that mix elements from lots of different markup languages without getting confused about what a particular element means.

> 4. In the xsl:stylesheet instruction,
> <xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
> the above namespace allows some XSL functions where as the older version 
> <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl"> 
> does not allow some   methods..
> so where are these methods?? how does the processor know 
> that the perticular 
> namespace doesnt contain the method or does contain the method??
> where does it 
> look for this information?

The XSLT processor has the methods (I assume you mean functions and elements) built into it. The XSLT processor knows which elements and functions it needs to use based on the namespace URI, which is hard-coded into the processor. The XSLT processor doesn't need to look anywhere to find this information -- it is programmed in by the implementer; the implementer found out what functions and elements need to be supported by reading the spec.

37.

Attributes, which namespace do they take up?

David Carlisle



> This is an aspect of namespaces that I've always been fuzzy on: if the
> default namespace is not xi (in this example), is an unqualified href
> attribute in the xi name space or is it in the default name space?

Neither. unprefixed attributes are always in no namespace, whatever the setting of the default namepsace (which only applies to elements)

38.

Is the current node an attribute?

Ken Holman

The test "count(../@*) = count(.|../@*)" is true only when the current node is an attribute.

39.

Conditionally adding attributes - or default

Michael Kay


>  How can i do a if condition in XSLT when a particular 
> attribute/element in xml is not present and else with a default string

> <customer name="xy" age="30">

> say incase age is not present. i want to replace with "unknown"
> <customer name="xy" age="unknown">

Here's one way:

<xsl:template match="customer">
  <xsl:attribute name="age">unknown</xsl:attribute>
  <xsl:copy-of select="@age"/>

this relies on the fact that if you add two attributes with the same name to a result element, the last one wins.

A more conventional solution is

<xsl:choose>
  <xsl:when test="@age"><xsl:copy-of select="@age"/></xsl:when>
  <xsl:otherwise><xsl:attribute name="age".....

40.

Attribute selection in the child axis

David Carlisle



Assuming the context is:
EVENT[6]
And there are 10 EVENT elements.

Why am I getting different results for the following counts:

<xsl:value-of select="count(following-sibling::EVENT)"/>
<xsl:value-of select="count(following-sibling::node())"/>

Assume the following XML:

<ROOT>
  <EVENT />
  <EVENT />
  <EVENT />
  <EVENT />
  <EVENT />
  <EVENT />
  <EVENT />
  <EVENT />
  <EVENT />
  <EVENT />
</ROOT>

Attributes are nodes, but node() doesn't match attribute nodes. true but that's nothing to do with the node() test, just that attributes are not in the child axis. A pattern of node() doesn't match attribute or document nodes because it is short for child::node() and attributes and / are not on the child axis. attribute::node() macthes attributes nodes.