Conditionally selecting node not working - templates

I'm just trying to select the node 'productgroep' with attribute value 'cd'. This is not working and I really don't understand why, I searched for answers but didn't find any.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="Oefening_8.xsl"?>
<catalogus>
<!-- cd catalogus -->
<productgroep type="cd">
<item referentienummer="7051444" catalogusnummer="1800022" EAN="0025218000222">
...
</productgroep>
<productgroep type="film">
<item referentienummer="8051445" catalogusnummer="2800023" EAN="5051888073650">
....
</productgroep>
</catalogus
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/">
<html>
<head>
<title>Oefening_8.xsl</title>
<meta charset="utf-8"/>
<link href="Oefening_8.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<h1></h1>
<xsl:template match="productgroep[#type='cd']">
</xsl:template>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

An <xsl:template/> cannot be a child of a <xsl:template/>, so your stylesheet is currently invalid and probably giving an error somewhere, depending on how you are using the XML and XSL.
One solution is to create the separate <xsl:template>s and use <xsl:apply-templates /> to processes the children of the source element.
<xsl:template match="/">
<html>
<head>
<title>Oefening_8.xsl</title>
<meta charset="utf-8"/>
<link href="Oefening_8.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<h1></h1>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="productgroep[#type='cd']">
<xsl:value-of select="item/#catalogusnummer"/> <!-- print #catalogusnummer for example -->
</xsl:template>

As #andyb pointed out, you can't have a template inside a template. It may be that you meant to use xsl:apply-templates where you have xsl:template, but that wouldn't have worked either with the path you used, because the current context there is the node above catalogus. Your options are to change the initial xsl:template to select the root element with either:
or
or to use the full path in the xsl:apply-templates:
I prefer the first option:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="/*">
<html>
<head>
<title>Oefening_8.xsl</title>
<meta charset="utf-8"/>
<link href="Oefening_8.css" type="text/css" rel="stylesheet"/>
</head>
<body>
<h1></h1>
<xsl:apply-templates select="productgroep[#type='cd']" />
</body>
</html>
</xsl:template>
</xsl:stylesheet>

Related

How do I excluding nodes from XLST

Sorry, this is a really novice question. My real problem involves translating HTML to Open Office XML, but this illustrates the issue I am seeing. I want to make sure the "b" node is ignored in my processing, i.e. not get the 123 at the end of the results output.
XML:
<?xml version="1.0"?><?xml-stylesheet type="text/xsl"?>
<a>
<hello-world>
<greeter>An XSLT Programmer</greeter>
<greeting>Hello, World!</greeting>
</hello-world>
<b>123
</b>
</a>
XSLT:
<?xml version="1.0"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="hello-world">
<HTML><HEAD><TITLE></TITLE></HEAD><BODY><H1>
<xsl:value-of select="greeting"/>
</H1>
<xsl:apply-templates select="greeter"/>
</BODY></HTML>
</xsl:template>
<xsl:template match="greeter">
<DIV>from <I><xsl:value-of select="."/></I></DIV>
</xsl:template>
</xsl:stylesheet>
Results:
<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<TITLE></TITLE>
</HEAD>
<BODY>
<H1>Hello, World!</H1>
<DIV>from <I>An XSLT Programmer</I></DIV>
</BODY>
</HTML>
123
I see you added an answer, but thought I could add more information.
Your first attempt was good, you could have simply added another template to ignore the "b" nodes.
<xsl:template match="b"/>
What was happening is that the XSLT built-in template rules include by default a template that copies the text of any node that's not explicitely matched by your templates.
Reference: docstore.mik.ua/orelly/xml/xmlnut/ch08_07.htm
See the output of your transformation with the added template: https://xsltfiddle.liberty-development.net/aixRus
The issue was that I wasn't selecting the whole document to begin with. If I change the xslt as follows, it then works
<?xml version="1.0"?><xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/*">
<xsl:apply-templates select='hello-world'/>
</xsl:template>
<xsl:template match="hello-world">
<HTML><HEAD><TITLE></TITLE></HEAD><BODY><H1>
<xsl:value-of select="greeting"/>
</H1>
<xsl:apply-templates select="greeter"/>
</BODY></HTML>
</xsl:template>
<xsl:template match="greeter">
<DIV>from <I><xsl:value-of select="."/></I></DIV>
</xsl:template>
</xsl:stylesheet>

XSLT Select attribute value of node with another attribute with a given value

I have a large number of html files like the following 01.html file:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>My Title</title>
</head>
<body>
<item itemprop="itemprop1" content="content1" />
<item itemprop="itemprop2" content="content2" />
<item itemprop="itemprop3" content="content3" />
<item itemprop="itemprop4" content="content4" />
<item itemprop="itemprop5" content="content5" />
<item itemprop="itemprop6" content="content6" />
<item itemprop="itemprop7" content="content7" />
<item itemprop="itemprop8" content="content8" />
<item itemprop="itemprop9" content="content9" />
</body>
</html>
There is only one item node with itemprop="itemprop1" in each html file. Same for itemprop2, itemprop3, etc.
I would like to have the following txt file output:
content1 | content 5
that is the concatenation of:
1. the value of the attribute content for the item with itemprop="itemprop1"
2. a pipe "|"
3. the value of the attribute content for the item with itemprop="itemprop5"
I run the following bash script:
xsltproc 01.xslt 01.html >> 02.txt
where 01.xslt is the following:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="body">
<xsl:value-of select="//item[#itemprop='itemprop1']/#content"/>|<xsl:value-of select="item[#itemprop='itemprop5']/#content"/>
</xsl:template>
</xsl:stylesheet>
Unfortunately it doesn't work. What is the correct xslt file?
UPDATE
This is the final working example.
01.html is the following:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>My Title</title>
</head>
<body>
<item itemprop="itemprop1" content="content1" />
<item itemprop="itemprop2" content="content2" />
<item itemprop="itemprop3" content="content3" />
<item itemprop="itemprop4" content="content4" />
<item itemprop="itemprop5" content="content5" />
<item itemprop="itemprop6" content="content6" />
<item itemprop="itemprop7" content="content7" />
<item itemprop="itemprop8" content="content8" />
<item itemprop="itemprop9" content="content9" />
</body>
</html>
01.xslt is the following:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes" method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="html">
<xsl:value-of select="//item[#itemprop='itemprop1']/#content"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="//item[#itemprop='itemprop5']/#content"/>
</xsl:template>
</xsl:stylesheet>
and the output 02.txt is the following:
content1|content5
Actually, XSTL processes XML files, not HTML.
Your source HTML almost meets requirements of well-formed
XML. There is only one error: Your meta element is not closed,
so I changed it to:
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
(adding / before the closing >).
Otherwise the XSLT processor displays an error message (at least in
my installation).
As far as your XSLT is concerned, I made a few corrections:
match="body" changed to match="html",
added // in the second xsl:value-of,
changed "bare" | to <xsl:text>|</xsl:text>, only for
readability reason (longer lines can not be seen on smaller
monitors),
added <xsl:output method="text"/> as your output does not
seem to be any XML.
Last 2 changes are optional, you can ignore them.
So the whole script can be like below:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="html">
<xsl:value-of select="//item[#itemprop='itemprop1']/#content"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="//item[#itemprop='itemprop5']/#content"/>
</xsl:template>
</xsl:stylesheet>
Your main problem using xsltproc is that you're trying to process HTML instead of XML. The difference is in the <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> tag which isn't closed and hence there is no valid XML for the XSLT processor (what results in an error). So add a closing char to make it
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
If you fix this problem and add a template that removes 'non-matching' text() nodes like
<xsl:template match="text()" />
your XSLT will do what you want.
<xsl:output method="text" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="html/body/item[#itemprop='itemprop1']/#content"/>|<xsl:value-of select="html/body/item[#itemprop='itemprop5']/#content"/>
</xsl:template>

XSL embed original XML file

I am transforming XML files with XSL to HTML files. Is it possible to embed the original XML file in the HTML output? When yes, how is that possible?
Update 1: To make my need better understandable: In my HTML file, I want a form where I can download the original XML file. Therefore I have to embed the original XML file into my HTML file (e.g. as a hidden input field)
Thanks
If you want to copy the nodes through you can simply do <xsl:copy-of select="/"/> where you want to insert them, however, putting arbitrary XML nodes into HTML does not make sense usually. If you want to serialize an XML document to plain text to render it then you can use solutions like http://lenzconsulting.com/xml-to-string/, for instance:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="http://lenzconsulting.com/xml-to-string/xml-to-string.xsl"/>
<xsl:output method="html" doctype-public="XSLT-compat" omit-xml-declaration="yes" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<html>
<head>
<title>Test</title>
</head>
<body>
<section>
<h1>Test</h1>
<xsl:apply-templates/>
<section>
<h2>Source</h2>
<pre>
<xsl:apply-templates mode="xml-to-string"/>
</pre>
</section>
</section>
</body>
</html>
</xsl:template>
<xsl:template match="data">
<ul>
<xsl:apply-templates/>
</ul>
</xsl:template>
<xsl:template match="item">
<li>
<xsl:apply-templates/>
</li>
</xsl:template>
</xsl:transform>
transforms an XML input like
<data>
<item att="value">
<!-- comment -->
<foo>bar</foo>
</item>
</data>
into the HTML
<!DOCTYPE html
PUBLIC "XSLT-compat">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test</title>
</head>
<body>
<section>
<h1>Test</h1>
<ul>
<li>
bar
</li>
</ul>
<section>
<h2>Source</h2><pre><data>
<item att="value">
<!-- comment -->
<foo>bar</foo>
</item>
</data></pre></section>
</section>
</body>
</html>

Generate html from xsl without DOCTYPE

Is it possible to generate html-output with xsl that has no doctype added to the output? If I don´t set any doctype myself it produces one on its own.
EDIT :
Since I don´t think that this is possible, I solved my problem bysimply cutting away the DOCTYPE after the html is generated with followiing regex: '<&!DOCTYPE[^>]*>'
Quickly tested with Saxon, yes this is possible...I'm not sure what xslt library you're using so it could be a symptom of that.
If I use Saxon to run this transform against ANY xml file (which generates a minimum viable HTML5 document, minus DOCTYPE) :
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="iso-8859-1" indent="yes"/>
<xsl:template match="/">
<html>
<head>
<title>Test</title>
</head>
<body>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
I get this output :
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>Test</title>
</head>
<body></body>
</html>

how can I add attribute to a XML CDATA Tags?

XML:
<?xml version="1.0" encoding="UTF-9" ?>
<mailAndMessageSettings>
<settings>
<add key="Url" value=""/>
<add key="UserName" value=""/>
<add key="Password" value=""/>
</settings>
<mail>
<subject>
Mp3 Submission
</subject>
<body>
<![CDATA[
<meta http-equiv="Content-Type" content="text/html; charset="utf-8""/>
<head></head>
<body>
<p>Hi,</p>
<p>Please find the attached mp3... :-)</p>
<p>here</p>
<p>Regards,</br>
Pete</p>
</body>
</html>
]]>
</body>
</mail>
</mailAndMessageSettings>
XSLT:
<xsl:template match="/">
<xsl:value-of select="/mailAndMessageSettings/mail" disable-output-escaping="yes"/>
</xsl:template>
Expected output:
<mail>
<subject>
Mp3 Submission
</subject>
<body>
<![CDATA[
<meta http-equiv="Content-Type" content="text/html; charset="utf-8""/>
<head></head>
<body>
<p>Hi,</p>
<p>Please find the attached mp3... :-)</p>
<p>here</p>
<p>Regards,</br>
Pete</p>
</body>
</html>
]]>
</body>
</mail>
I want to add an attribute "onclick" on "here" in a CDATA and getting the whole "mail" node? Is it really possible? Can anyone help me with this stuff? Thanks in advance.
Your help would be greatly appreciated :)
There are no nodes or tags inside a CDATA section. CDATA means "character data". The only reason for putting stuff inside CDATA is to say "The stuff in here might look like markup, but I don't want it treated as markup; just treat it as text". So if you want to treat it as markup, don't put it in CDATA.
You'll have to resort to string manipulation, like so:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="body[contains(.,'>here</a>')]">
<xsl:value-of disable-output-escaping="yes" select="concat(
'<![CDATA[',
substring-before(.,'>here</a>'),
' onclick="myfunction();">here</a>',
substring-after(.,'>here</a>'),
']]>'
)"/>
</xsl:template>
</xsl:stylesheet>
But is there a reason why the mail body has to be CDATA in the first place?