Sebastian Rahtz
<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
Ben Robb
Use the xsl:attribute call in your style sheet
and then treat it like a normal <A href=...> tag.
<xsl:attribute name="href">mailto:<xsl:value-of select="email"/>
</xsl:attribute>
Mail us!
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"/>
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>
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'">
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>
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.
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.
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>
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]" ); ?>"/>
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>
Linda van den Brink
Q: Expansion
for an element,
<SLIDE
REF="d://programs//xml//astron//documents//Hist_1.xml">History</SLIDE>
<xsl:template match="SLIDES">
<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 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 elements that have a REF attribute. To get the attribute value itself, use:
<xsl:template select="SLIDE/@REF"/>
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.
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}"/>
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"/>
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 /></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.
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>
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="@*/>
David Carlisle
<xsl:if test="contains(@CLAS,'X')">
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>
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.
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>
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 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.
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 ....
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)">
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"/>
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 is about two ways of doing it :
- - One using the comparison with the preceding element in a sorted subtree,
with a sub-template. This give a solution like this
from Robert Stupak"
<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 compacity and probably 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.
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.
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>