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=" 1 2 3 "
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><?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> 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 entity will generate an undefined entity
unless you have defined it in the stylesheet. Use  .
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 "<", ">", 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 < > and &.
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>
</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 (" or '). 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, ' and " 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('"',"'")]" 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('"',"'")]"
Yes, the working template is a bear to decipher, but it does work.
- ---XML Source ---
<channel>
<location name=' "x" 'a z'/>
</channel>
- ---XSLT ---
<xsl:template
match="channel/location[@name=concat(' "x" ',"
'a z")]">
<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=".>0">
<Font color="green">
<xsl:value-of select="format-number(.,'##.##')"/>
</Font>
</xsl:when>
<xsl:when test=".<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. |