xslt default template confusion for root node - xslt

One of the default templates in xslt is the following:
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
Why does the match pattern contains the expression for the root node "/"? Doesn't the asterisk "*" capture already all nodes in the document?
I tried to leave it out and there was no difference. Following xslt
<xsl:stylesheet
version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xhtml" indent="yes"/>
<xsl:template match="*">
<xsl:value-of select="./name()"/>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
and following xml
<?xml version="1.0" encoding="UTF-8"?>
<a>
<b>
</b>
</a>
produces the output:
<?xml version="1.0" encoding="UTF-8"?>a
b
So the root node a gets captured.

As the default axis for a location path is child, *|/ is just an abbreviation for child::*|/. And child::* does not match the root node of a document, just its children.

Related

XSLT select // and .//

What are the difference between // .// when we set path?
And the second question if I want to get some information of parent node in the for-each block how I can get it?
// is short for descendant axis.
If you say //para, it selects all para elements in the whole document.
When you say .//para, all the para elements which are descendants of the context node are selected.
For a demonstration, consider this XML:
<l1>
<para>1</para>
<l2>
<para>2</para>
<l3>
<para>3</para>
</l3>
</l2>
</l1>
and this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/l1">
<output>
<xsl:apply-templates select="l2"/>
</output>
</xsl:template>
<xsl:template match="l2">
<para-all>
<xsl:copy-of select="//para"/>
</para-all>
<para-context>
<xsl:copy-of select=".//para"/>
</para-context>
<parent>
<xsl:value-of select="name(parent::*)"/>
</parent>
</xsl:template>
</xsl:stylesheet>
And the output you will get is:
<?xml version="1.0" encoding="utf-8"?>
<output>
<para-all>
<para>1</para>
<para>2</para>
<para>3</para>
</para-all>
<para-context>
<para>2</para>
<para>3</para>
</para-context>
<parent>l1</parent>
</output>
The para-all gets all the paras in the document, whereas para-context gets only those paras which are descendant of l2(the context node)
And about how to select the parent, as in the code(see parent element in XSLT).
In the example above, the context node is l2 and by using parent axis, the parent element's name is being written.
You can use either parent::* or ../

XSL check multiple nodes exist with for-each

If I have multiple nodes in an xsl document and want to check that they all have a child node that exists, how would you do that with a for-each loop in XSL 2?
<A>
<B>
<C>test</C>
</B>
<B>
<C>test</C>
</B>
</A>
For example in the document above, we want to iterate through all B Nodes in the document and ascertain if C exists with the value 'test' for that B node.
"we want to iterate through all B Nodes in the document and ascertain if C exists with the value 'test' for that B node"
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="A/B[C='test']">
<!-- Rest of XSLT -->
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You can add 'tests'/predicates using [].

XSLT not matching element - namespace declarations

I'm sure this is a very simple fix, but I'm stumped. I've got input XML with the following root element, and repeating child elements:
<modsCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.loc.gov/mods/v3"
xsi:schemaLocation="
http://www.loc.gov/mods/v3
http://www.loc.gov/standards/mods/v3/mods-3-4.xsd">
<mods version="3.4">
...
I've got an XSLT sheet with the following to match each <mods> node, and kick it out as a separate file named by an <identifier type="local"> element.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.loc.gov/mods/v3">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/modsCollection">
<xsl:for-each select="mods">
<xsl:variable name="filename"
select="concat(normalize-space(
identifier[#type='local']),
'.xml')" />
<xsl:result-document href="{$filename}">
<xsl:copy-of select="."></xsl:copy-of>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This works if the XML input does not have the xmlns:xsi, xmlns, or xsi:schemaLoaction attributes in the root element. So, for example, it works on the following:
<modsCollection>
<mods version="3.4">
...
I know that some of our MODS files have had the prefix included but I'm unclear why this won't work without the prefix if our XSLT matching is not looking for the prefix. Any thoughts or advice would be greatly appreciated.
<xsl:template match="/modsCollection">
matches modsCollection in no namespace. You want
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.loc.gov/mods/v3"
xmlns:m="http://www.loc.gov/mods/v3">
then
<xsl:template match="/m:modsCollection">
To match modsCollection in the mods namespace, and similarly use the m: prefix in all xslt patterns and xpath expressions in the stylesheet.

XSLT node Traversal

Here is a snip-it of the XML:
<?xml version="1.0" encoding="iso-8859-1" ?>
<NetworkAppliance id="S123456">
<Group id="9">
<Probe id="1">
<Value>74.7</Value>
</Probe>
</NetworkAppliance>
I want to get the single point value of 74.7. There are many groups with unique ID's and many Probes under that group with unique ID's each with values.
I am looking for example XSLT code that can get me this one value. Here is what i have that does not work:
<?xml version="1.0" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" version="3.2" />
<xsl:template match="NetworkAppliance">
<xsl:apply-templates select="Group[#id='9']"/>
</xsl:template>
<xsl:template match="Group">
Temp: <xsl:value-of select="Probe[#id='1']/Value"/>
<br/>
</xsl:template>
</xsl:stylesheet>
Here is what worked for me in the end:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Edited by XMLSpy® -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:for-each select="NetworkAppliance/Group[#id=9]/Probe[#id=1]">
Value: <xsl:value-of select="Value" />
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Don't forget that you can do select several levels at once. Fixing your XML to:
<?xml version="1.0" encoding="iso-8859-1" ?>
<NetworkAppliance id="S123456">
<Group id="9">
<Probe id="1">
<Value>74.7</Value>
</Probe>
</Group>
</NetworkAppliance>
and using this stylesheet:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" version="3.2" />
<xsl:template match="/">
Temp: <xsl:value-of select="//Group[#id='9']/Probe[#id='1']/Value"/>
<br/>
</xsl:template>
</xsl:stylesheet>
we can pick out that one item you're interested in.
Points to note:
The // part of the expression means that the search for Group nodes takes place throughout the whole tree, finding Group nodes at whatever depth they're at.
The [#id='9'] part selects those Group nodes with id of 9
The Probe[#id='1'] part immediately after that selects those children of the Group nodes it found where the id is 1, and so on.
<xsl:value-of select="/NetworkAppliance/Group[#id=9]/Probe[#id=1]/Value"/>
XSLT is just one of the tools in the box, and nothing without XPath.
the xpath for value of a node is /node/text()
So
<xsl:value-of select="Probe[#id='1']/text()"/>

xslt apply-templates selects all remaining textnodes

I have this simplified xml:
<?xml version="1.0" encoding="UTF-8"?>
<a>
<b>
<c>
<d>1</d>
<e>2</e>
</c>
</b>
<f>
<g>3</g>
</f>
</a>
This is the xslt i try to apply:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="a">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="b">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="c">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="d">
</xsl:template>
</xsl:stylesheet>
When I apply this sheet, I get output 2 3, which are the remaining textnodes. I've read about the built-in templates which get applied if it can't find a matching template, but in this case, it should find a template?
What is going on?
Edit:
In this case, i would expect to see nothing, because the templates are empty. But i get 2 3 in stead.
When you do <xsl:template match="d">, you tell the processor to ignore all nodes under <d>.
All other nodes are processed with default rules, including the text() one, which is to print the text.
That's why you see 23, and not 1.
Start from the root:
<xsl:template match="/a">
And specify either a mode (so that the default template does not get called, because it does not find a template for e, f and g) or define your own * template which does nothing at the end of the stylesheet:
<xsl:template match="*"/>