Look at the XSLT-code under the address http://www.w3schools.com/xml/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog_apply ... Below you find the first part of this code (and the one decisive for my question):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
If you now change only the line
<xsl:apply-templates/>
to
<xsl:apply-templates select="cd"/>
the transformation does not work anymore ... (The code now looks as follows:)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<xsl:apply-templates select="cd"/> <!--ONLY LINE OF CODE THAT WAS CHANGED-->
</body>
</html>
</xsl:template>
My question is: Why does the change break the code? In my opinion, the logic is the same in both cases:
apply the template matching "cd"
inside template "cd" apply the other two templates ("title" + "artist")
UPDATE:
The whole xslt code is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>My CD Collection</h2>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="cd">
<p>
<xsl:apply-templates select="title"/>
<xsl:apply-templates select="artist"/>
</p>
</xsl:template>
<xsl:template match="title">
Title: <span style="color:#ff0000">
<xsl:value-of select="."/></span>
<br />
</xsl:template>
<xsl:template match="artist">
Artist: <span style="color:#00ff00">
<xsl:value-of select="."/></span>
<br />
</xsl:template>
</xsl:stylesheet>
Here's an excerpt from the xml:
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
<artist>Bob Dylan</artist>
<country>USA</country>
<company>Columbia</company>
<price>10.90</price>
<year>1985</year>
</cd>
<cd>
<title>Hide your heart</title>
<artist>Bonnie Tyler</artist>
<country>UK</country>
<company>CBS Records</company>
<price>9.90</price>
<year>1988</year>
</cd>
......
</catalog>
What W3C schools doesn't tell you is about XSLT's Built-in Template Rules.
When you do <xsl:apply-templates select="cd"/> you are positioned on the document node, which is the parent of the catalog element. Doing select="cd" will select nothing, because cd is a child of the catalog element, and not a child of the document node itself. Only catalog is a child.
(Note that catalog is the "root element" of the XML. An XML document can have only one root element).
However, when you do <xsl:apply-templates />, then this is equivalent to <xsl:apply-templates select="node()" /> which will select the catalog element. This is where the built-in templates kick in. You don't have a template matching catalog in your XSLT, and so the built-in one is used.
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
(Here * matches any element). Thus, this built-in template will select the child nodes of catalog, and so match the other templates in your XSLT.
Note that, in your second example, you can change the template match to this...
<xsl:template match="/*">
This will match the catalog element, and so then <xsl:apply-templates select="cd" /> will work.
Related
My XML
<?xml version="1.0" encoding="UTF-8"?>
<catalog>
<cd>
<title>
<name>Empire Burlesque1 </name>
</title>
</cd>
<cd>
<title>
<name>Empire Burlesque 2</name>
</title>
</cd>
<cd>
<title>
<name>Empire Burlesque 2</name>
</title>
</cd>
</catalog>
XSL
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="catalog/cd">
<xsl:call-template name="currentValue" />
<xsl:call-template name="prevValue" />
</xsl:for-each>
</xsl:template>
<xsl:template name="currentValue">
<xsl:value-of select="title/name" />
</xsl:template>
<xsl:template name="prevValue">
<xsl:value-of select="preceding-sibling::title[name][1]" />
</xsl:template>
</xsl:stylesheet>
The preceding sibling does not print anything. I want to store both in different variables and compare them. Can you help me pointing what is wrong here?
Currently, when you are checking for the preceding element you are positioned on cd element, and that only has other cd elements as siblings. Therefore, the expression you want is this:
<xsl:value-of select="preceding-sibling::cd[1]/title/name" />
Find the following line in XSL below:
<xsl:variable name="id" select="concat('CDTableCell', position())" />
<span id="{$id}"
I'm not getting a unique id for each of the spans after the transform. Any ideas? Shows the same id's.
xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<head>
<script>
function mycolorcontrastfx(bgColor)
{
return '#000000';
}
</script>
</head>
<body>
<h2>My CD Collection</h2>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="cd">
<p>
<xsl:apply-templates select="title"/>
<xsl:apply-templates select="artist"/>
</p>
</xsl:template>
<xsl:template match="title">
Title:
// Credit for help: http://stackoverflow.com/a/43546704/139698
// As well as W3Schools
<xsl:variable name="id" select="concat('CDTableCell', position())" />
<span id="{$id}" style="background-color:#ff0000">
<xsl:value-of select="."/>
</span>
<br />
<script>
var tdElem = document.getElementById('<xsl:value-of select="$id" />')
var bgColor = tdElem.style.backgroundColor;
var textColor = mycolorcontrastfx(bgColor);
tdElem.style.color = textColor;
</script>
</xsl:template>
</xsl:stylesheet>
xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="blackorwhite.xslt"?>
<catalog>
<cd>
<title>Empire Burlesque</title>
</cd>
<cd>
<title>Hide your heart</title>
</cd>
</catalog>
I'm not getting a unique id for each of the spans after the transform.
You're not getting unique ids, because each title is the first child of its parent cd. I suggest you use the generate-id() function instead of position() (as you were advised originally).
I'm expecting only Hola to appear:
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="helloworld.xslt"?>
<greetings>
<greeting id="1">
Hello World!
</greeting>
<greeting id="2">
Hola!
</greeting>
</greetings>
However, both greetings appear.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="html"/>
<xsl:template match="greetings">
<xsl:apply-templates select="greeting"/>
</xsl:template>
<xsl:template match="greeting">
<html>
<body>
<h1>
<xsl:value-of select="#id[.>1]"/>
</h1>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
It is not so much the conditional that is causing a problem, but the fact your statement is selecting the attribute, and so the xsl:value-of will output the attribute (but only if the value is greater than 1)
What you need to do is move the conditional to your xsl:apply-templates, and then do <xsl:value-of select="." /> to get your "Hola" value
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="greetings">
<xsl:apply-templates select="greeting[#id > 1]"/>
</xsl:template>
<xsl:template match="greeting">
<html>
<body>
<h1>
<xsl:value-of select="."/>
</h1>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I have xml like:
<item id="1">
<items>
<item id="2">Text2</item>
<item id="3">Text3</item>
</items>Text1
</item>
How to return text of <item id="1">('Text1')?
<xsl:value-of select="item/text()"/> returns nothing.
My XSLT is:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="item"/>
</body>
</html>
</xsl:template>
<xsl:template match="item">
<xsl:value-of select="text()"/>
</xsl:template>
</xsl:stylesheet>
I dont know what else to type to commit my edits
How to return text of <item id="1">('Text1')? <xsl:value-of
select="item/text()"/> returns nothing.
The item element has more than one text-node children and the first of them happens to be a all-whitespace one -- this is why you get "nothing".
One way to test if the string value of a node isn't all-whitespace is by using the normalize-space() function.
In a single Xpath expression, you want this:
/*/text()[normalize-space()][1]
Here is a complete transformation the result of which is the desired text node:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:copy-of select="text()[normalize-space()][1]"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<item id="1">
<items>
<item id="2">Text2</item>
<item id="3">Text3</item>
</items>Text1
</item>
the wanted, correct result is produced:
Text1
This should generally work:
<xsl:apply-templates select="item/text()" />
Incorporated into your XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="item_key" match="item" use="."/>
<xsl:strip-space elements="*" />
<xsl:template match="/">
<html>
<body>
<ul>
<xsl:apply-templates select="item"/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match="item">
<li>
<xsl:apply-templates select="text()"/>
</li>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, the result is:
<html>
<body>
<ul>
<li>Text1
</li>
</ul>
</body>
</html>
Alternatively, this should work as well:
<xsl:copy-of select="item/text()" />
I have quite a large XSL document for an assignment that does a number of things. It is nearly complete but I missed a requirement that it has to be sorted and I cannot get it working. Here is a SSCCE of what is happening.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Root Document -->
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="staff">
<xsl:sort select="member/last_name" />
</xsl:apply-templates>
</body>
</html>
</xsl:template>
<xsl:template match="member">
<xsl:value-of select="first_name" /> <xsl:value-of select="last_name" /> <br/>
</xsl:template>
</xsl:stylesheet>
The XML file looks like this
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="sort.xsl"?>
<staff>
<member>
<first_name>Joe</first_name>
<last_name>Blogs</last_name>
</member>
<member>
<first_name>John</first_name>
<last_name>Smith</last_name>
</member>
<member>
<first_name>Steven</first_name>
<last_name>Adams</last_name>
</member>
</staff>
I was expecting the staff members to be listed by last name but they are not getting sorted. Please bear in mind that I am very inexperienced at XSLT.
<xsl:apply-templates select="staff">
<xsl:sort select="member/last_name" />
</xsl:apply-templates>
selects the staff elements and sorts them, but there is only one staff element, so this is a no-op.
Change to
<xsl:apply-templates select="staff/member">
<xsl:sort select="last_name" />
</xsl:apply-templates>
then that selects all the member elements and sorts them.
what is missing is a staff matching template or change the matching template to member like in this one:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Root Document -->
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates select="staff/member">
<xsl:sort select="last_name" />
</xsl:apply-templates>
</body>
</html>
</xsl:template>
<xsl:template match="member">
<xsl:value-of select="first_name" /> <xsl:value-of select="last_name" /> <br/>
</xsl:template>
</xsl:stylesheet>