Q: Why aren't my namespaces being inherited?
Some time ago I read something about namespace declarations which said, "The namespace declaration given for the current element is also valid for all its children and descendants."
Please see my XML and XSLT below. According to the above, can
I treat (in XSLT) a title element as
fa:title and fb:title? When I try to do
so, viewing the XML in Internet Explorer (IE), I can't see the
expected result.
My XML (test.xml):
<?xml-stylesheet type="text/xsl"
href="main.xsl"?>
<root>
<head xmlns:fa="http://www.test.com/element-a">
<title>I am in file 'fa'</title>
</head>
<head xmlns:fb="http://www.test.com/element-b">
<title>I am in file 'fb'</title>
</head>
</root>
My XSLT stylesheet (main.xsl):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.test.com/xml"
xmlns:fa="http://www.test.com/element-a"
xmlns:fb="http://www.test.com/element-b">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<body>
<p style="color:red">FA:
<xsl:value-of
select="string(//*/*/fa:title)"/>
</p>
<p style="color:blue">FB:
<xsl:value-of
select="string(//*/*/fb:title)"/>
</p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Please advise, how do I achieve this? Is my understanding wrong?
A: Yes, your understanding is wrong... but understandably so. First, let's deal with that quote you remember. According to the Namespaces in XML 1.1 Recommendation (emphasis added):
The scope of a namespace declaration declaring a prefix extends from the beginning of the start-tag in which it appears to the end of the corresponding end-tag... Such a namespace declaration applies to all element and attribute names within its scope whose prefix matches that specified in the declaration.
That is, the declaration doesn't automatically "trickle down" to apply to all subordinate element and attribute names -- just to the ones which share the prefix named in the declaration.
In your case, your stylesheet does these two things related to namespaces:
- includes appropriate namespace declarations for both the
fa:andfb:prefixes -- and also for the default namespace (that is, covering those cases in which element/attribute names have no prefix); and - seems to distinguish between two forms of the
titleelement, one prefixedfa:and the other prefixedfb:.
About this second bulleted item: You've set up two
xsl:value-of elements, whose select
attributes point several levels down in the document tree to an
fa:title element in one case, and an
fb:title element in the other. I'll come back to
discuss those location paths in a moment. But, for now, consider
(in light of the above quote from the Namespaces Recommendation)
what you're asking the XSLT processor to search for: elements in
the source tree (test.xml) literally named fa:title
and fb:title -- including the prefixes. But test.xml
has no such elements; the namespace declarations which it
contains apply only to element names prefixed
fa: and fb:, respectively. Even
though your two title elements are subordinate to
head elements declaring those namespaces, the best
you can say is that the namespace declarations are
potentially in effect but actually unused by each
title element.
Thus, in IE, your document looks something like the partial screen capture in Figure 1:

The two literal text strings show up correctly, but the
xsl:value-of elements come up empty-handed. As you
say, this isn't what you want.
There are a couple of ways to fix this. Which you use depends on what you're trying to achieve in a larger sense, how much control you have over the source document and the corresponding stylesheet, and, perhaps, on your tastes.
Probably the most straightforward way to fix it is to add the
appropriate prefixes to the title elements in
test.xml; this approach has the advantage of requiring no changes
at all to your stylesheet. Those portions of test.xml would now
look like the following:
<head
xmlns:fa="http://www.test.com/element-a">
<fa:title>I am in file
'fa'</fa:title>
</head>
<head xmlns:fb="http://www.test.com/element-b">
<fb:title>I am in file
'fb'</fb:title>
</head>
Alternatively, you might think of tinkering with the stylesheet. Before dealing with the namespace-related question, though, I'd like to suggest some ways to clean up your stylesheet a little.
For starters, you don't need to use the string()
function to return the string-value of your location path. Because the
last step in each location path is the name of an element,
the xsl:value-of elements' select attributes
will automatically return the string-value of the selection. Thus,
factoring in that you don't need the (useless) namespace prefixes, the
central portion of your stylesheet will now look like this:
<p style="color:red">FA:
<xsl:value-of
select="//*/*/title"/>
</p>
<p style="color:blue">FB:
<xsl:value-of
select="//*/*/title"/>
</p>
Secondly, the location paths themselves are more complicated
than they need to be. All those intervening slashes and asterisks
can be replaced by a single reference to the
descendant:: axis, in this manner:
<p style="color:red">FA:
<xsl:value-of
select="descendant::title"/>
</p>
<p style="color:blue">FB:
<xsl:value-of
select="descendant::title"/>
</p>
(The context node at the point of the
xsl:value-of elements, established by the
xsl:template element's match element,
is the document root. Therefore, in order to get all the
title (or any other) elements in the document, you
don't need to reset the context to the document root -- which is
what your opening slash does -- and then laboriously walk the
tree down to a specific level.)
A quasi-shortcut to using the descendant:: axis
like this would be to replace it with a single double slash:
<p style="color:red">FA:
<xsl:value-of select="//title"/>
</p>
<p style="color:blue">FB:
<xsl:value-of select="//title"/>
</p>
(Note: The double slash actually is a shortcut for the
descendant-or-self:: axis, which can be a
performance drag when processing large XML source documents but
can be useful for small "play" documents like yours. As you'll
see in a moment, though, using the // can produce
very different results even for small documents.)
Whichever approach you use to cleaning up the XPath location path, alas, you end up with a still not-right result, as depicted in Figure 2:

At this point, the problem is that both XPath location paths
in both xsl:value-of elements are identical. They
both locate the node-set consisting of all title
elements in the source tree -- and the string-value of any such
node-set is always the string-value of the first node in the set
(the first title element, in this case).
Now what?
|
Also in XML Q&A | |
Unfortunately, assuming you really do want to use namespaces
to locate the two different title elements -- well,
it just isn't possible. The reason: again, while you've declared
the namespaces, they aren't actually in use in the source tree
(by any element, not just the two titles). So
there's nothing namespace-related with which to distinguish the
two title elements.
You can still distinguish them, of course, using a somewhat
clunky solution such as selecting them based on their positions.
In order to do so, you'll have to drop the //
shortcut and revert to using the descendant:: axis,
like this:
<p style="color:red">FA:
<xsl:value-of
select="descendant::title[1]"/>
</p>
<p style="color:blue">FB:
<xsl:value-of
select="descendant::title[2]"/>
</p>
(If you used the // shortcut here, you'd be
telling the XSLT processor to locate, respectively, (a) any
title elements which are the first child of their
parents and (b) any ones which are the second child of their
parents. There is no match for condition (b), though. As a
result, all you'd get would be the "FA: I am in file 'a'" string
for the FA paragraph in the result tree, and plain old "FB:" for
the FB paragraph.)
As desired, your output viewed in IE now looks like Figure 3:

This may feel like cheating, though, because you still haven't solved the namespace question.
The bottom line: in order to process namespaces in an XSLT
stylesheet, you must not only declare the URIs associated with
their prefixes (like your
xmlns:fb="http://www.test.com/element-b"
declaration). You must also actually declare and use the
prefixes in the source document.
As I suggested previously, you can tack the fa:
and fb: prefixes onto the names of the
title elements in test.xml, leaving your stylesheet
in its original form. Alternatively, you could apply those
prefixes to the title elements' parents -- the two
head elements:
<fa:head
xmlns:fa="http://www.test.com/element-a">
<title>I am in file 'fa'</title>
</fa:head>
<fb:head
xmlns:fb="http://www.test.com/element-b">
<title>I am in file 'fb'</title>
</fb:head>
Then your stylesheet could be constructed like this,
referencing each [prefix]:head element and
the corresponding (unprefixed) title:
<p style="color:red">FA:
<xsl:value-of
select="descendant::fa:head/title"/>
</p>
<p style="color:blue">FB:
<xsl:value-of
select="descendant::fb:head/title"/>
</p>
Results in this case are identical to those shown in Figure 3, above.
By the way, you may wonder about the possibility of somehow using the default namespace, associated in your stylesheet with the URI http://www.test.com/xml. This won't work under XSLT 1.0. By a strange fluke in the spec, you can't match on the default namespace without using a prefix at all; but of course as soon as you use a prefix, an element or attribute name is no longer in the default namespace. This anomaly is expected to be addressed in the XSLT 2.0 standard which is, as of this writing, still in "last call" status.
Share your experience in our forum.
(* You must be a member of XML.com to use this feature.)
Comment on this Article
| Titles Only | Titles Only | Newest First |
- #1 Santa Monica Locksmith Los Angeles 1-310-925-1720
2009-06-11 15:16:35 whats [Reply]
#1 Santa Monica Locksmith Los Angeles 1-310-925-1720
#1 24 Hour Locksmith Los Angeles
call 1-323-678-2704
Locksmith services Los Angeles, including locks installation, doors locks repair, doors locks rekey, locks and keys products or services the best value and commitment to customers 100 satisfaction guaranteed.
24 Hour Emergency Locksmiths Service
Burglary Repairs Los Angeles County
Professional Lock Repair
Professional Door Lock Replacement
Professional Lockout Services
Professional Door Locks Rekeying
Immediate Response 24 hours a day
Doors Locks Installation
Automotive Locksmith
- Question on namespaces
2004-08-09 16:35:50 cord_thomas [Reply]
I am trying to create an XSLT transform for a jabber-produced file and have namespace problem that i believe this article addresses, but i cannot figure out the answer. Following are snips of the xml and my attempted xslt - where am i going wrong:
<xdb>
<foo xdbns="jabber:jud:users" xmlns="jabber:jud:users">
<item jid="some@im.rand.org">
<key/>
<name>a person</name>
<first>a</first>
<last>person</last>
<nick/>
<email/>
</item>
<foo>
<xdb>
XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ja="jabber:jud:users">
<xsl:output method="html"/>
<xsl:param name="i_eid"/>
<xsl:template match="/">
<HTML>
<HEAD> <LINK REL="stylesheet" TYPE="text/css" HREF="css/tipic.css"/>
</HEAD>
<BODY>
<table class="tbb">
<tr class="trIM">
<td class="thIM" width="220">IM UserName</td>
<td class="thNick" width="220">Nick Name</td>
</tr>
<xsl:apply-templates select="*/ja:foo/item"/>
</table>
</BODY>
</HTML>
</xsl:template>
<xsl:template match="ja:item">
<tr>
<td>
<xsl:value-of select="@jid"/>
</td>
<td>
<xsl:value-of select="nick"/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
I am not clear on:
1 - the xpath relationship between the match name i put in under apply-templates, and the one i put in under the template match
2 - where i specify my ja: namespace prefix
This XSLT results in no data -
if i take out the jabber xmlns declaration in the source XML file, my XSLT transforms nicely, but i have no control over the XML source file.
- Question on namespaces
2005-01-06 12:55:47 mrylander@gmail.com [Reply]
Just make the change below to your "xsl:apply-templates" line:
<xsl:apply-templates select="*/ja:foo/ja:item"/>
Because the jabber doc has a default namespace you need to provide a prefix in the XSLT, which you do. But you need to use that prefix on all elements in the default namespace, which includes the 'item' elements.
Hope that helps!
- Question on namespaces
- use (or not) of the namespace axis
2004-07-01 03:49:18 David Carlisle [Reply]
Unfortunately, assuming you really do want to use namespaces to locate the two different title elements -- well, it just isn't possible. The reason: again, while you've declared the namespaces, they aren't actually in use in the source tree (by any element, not just the two titles). So there's nothing namespace-related with which to distinguish the two title elements.
This isn't really true, the namespace declarations are in scope and are visible from the title elements, so the original stylesheet
would work as desired if changed to say
<xsl:value-of
select="string(//*/*/title[namespace::fa])"/>
FB:
<xsl:value-of
select="string(//*/*/title[namespace::fb])"/>
That is, instead of looking for fa:title look for a title in no-namespace that has a namespace binding for "fa" in scope.
- couple of typos
2004-07-01 01:41:19 Brian Ewins [Reply]
After: "Probably the most straightforward way to fix it is to add the appropriate prefixes to the title elements in test.xml..."
the sample has fa:title for both title elements, the second should be fb:title. There's also spurious semicolons in that fragment, immediately after each namespace declaration.
- couple of typos
2004-07-02 07:50:55 Brian Ewins [Reply]
(typos nearly nailed - there's still this: <fb:title>I am in file'fb'</fa:title> <-- here)
Sometimes the standards seem designed to create problems. A nasty feature of XML Namespaces that 'no prefix' doesn't mean 'default namespace', it means 'default namespace' OR 'no namespace', and the difference is important. Xml schema cements this by making elementFormDefault='unqualified' the default, which means this:
<ns:foo xmlns:ns="some:schema-ns"><bar>Hello world</bar></ns:foo>
... is actually referring to 'bar' in the same schema as 'foo' (but /not/ the same namespace as foo - 'bar' has no namespace above). Or maybe it doesn't. You can't actually tell without resorting to the schema.
As Humpty-Dumpty said to Alice: "When I use a word, it means just what I choose it to mean -- neither more nor less."
- couple of typos
