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>
Related
Here is my input xml
<?xml version="1.0" encoding="utf-8"?>
<content>
<body>
<p>This is a Test</p>
<p>
Toronto, ON - Text added here.
</p>
</body>
</content>
and here is my style sheet
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" indent="no" />
<!-- Root / document element-->
<xsl:template match="/">
<xsl:apply-templates select="node()"/>
</xsl:template>
<xsl:template match="*/text()">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
</xsl:stylesheet>
When i apply this transformation using the ASP.NET XslCompiledTransform's transform method and view the result in the browser, I still can see the spaces and the normalize-space does not seem to be working.
Can any one please let me know what i am doing wrong
Thanks a lot!
This transformation:
<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="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(text()[2])]/text()">
<xsl:value-of select="normalize-space()"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided source XML document:
<content>
<body>
<p>This is a Test</p>
<p>
Toronto, ON - Text added here.
</p>
</body>
</content>
produces the wanted, correct result:
<content><body><p>This is a Test</p>
<p>Toronto, ON - Text added here.</p>
</body>
</content>
Update:
In case the problem is caused by a space-like character(s), here is a solution that will replace the unwanted (up to 40 per text node) characters with spaces and then normalize-space() would do its work:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vAllowed" select=
"concat('ABCDEFGHIJKLMNOPQRSTUVWXUZ', 'abcdefghijklmnopqrstuvwxyz',
'0123456789.,-')"/>
<xsl:variable name="vSpaces" select="' '"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(text()[2])]/text()">
<xsl:value-of select=
"normalize-space(translate(., translate(.,$vAllowed,''), $vSpaces))"/>
</xsl:template>
</xsl:stylesheet>
When the transformation is applied on this source XML document:
<content>
<body>
<p>This \\\ is a ~~~ Test</p>
<p>
Toronto, ``` ON - Text added here.
</p>
</body>
the wanted, correct result is produced:
<content>
<body>
<p>This is a Test</p>
<p>Toronto, ON - Text added here.</p>
</body>
</content>
Dimitre's answer is of course great, but there is a typo. In $vAllowed he's missing a capital "Y."
Also for my own purposes, I needed a single quote or apostrophe to be an allowed character. I added it to the variable like so:
<xsl:variable name="vAllowed" select=
"concat('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz',
'0123456789.,-', '''')"/>
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>
So this may be a strange request but I'll give it a go. I have an xml document
<?xml version="1.0" encoding="utf-8" ?>
<Page>
<ID>Site</ID>
<Object>
<ID>PostCode</ID>
<Type>div</Type>
<Class>display-label</Class>
<Value>PostCode</Value>
</Object>
<Object>
<ID>PostCodeValue</ID>
<Type>div</Type>
<Class>display-field</Class>
<Value>$PostCode</Value>
</Object>
</Page>
and I'm using this XSL to transform it into a html page
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
</head>
<body>
<xsl:for-each select="Page/Object">
<<xsl:value-of select="Type"/>>
<xsl:value-of select="Value"/>
</<xsl:value-of select="Type"/>>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
As you can see I'm trying to generate the correct html tag dependent on the type node in the xml. The problem is "<" isn't accepted in the xsl and encoding it stops it from being recognised as a tag.
Any suggestions how I'd go about this?
Thanks in advance
Use xsl:element to create an element node in the output document.
<xsl:element name="{Type}" >
<xsl:value-of select="Value"/>
</xsl:element>
Note: the curly braces in the name attribute may look strange, if you aren't familiar. It is an Attribute Value Template used to evaluate an XPATH expression inside of an attribute declaration.
Applied to your stylesheet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
</head>
<body>
<xsl:for-each select="Page/Object">
<xsl:element name="{Type}" >
<xsl:value-of select="Value"/>
</xsl:element>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
you need to wrap your "<" in a text node with disable-output-escaping like this:
<?xml version="1.0" encoding="utf-8"?><!-- DWXMLSource="temp.xml" -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
</head>
<body>
<xsl:for-each select="Page/Object">
<xsl:text disable-output-escaping="yes"><</xsl:text><xsl:value-of select="Type"/><xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:value-of select="Value"/>
<xsl:text disable-output-escaping="yes"></</xsl:text><xsl:value-of select="Type"/><xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
this question relates to
XSLT to generate html tags specified in XML
where I had an xml docment and used an xsl to generate html tags using
<xsl:element name="{Type}" >
the problem I have is that I want to specify some html attributes in my xml for example
<Page>
<ID>Site</ID>
<Object>
<ID>PostCode</ID>
<Type>div</Type>
<Attributes>
<Attribute name="class">TestStyle</Attribute>
<Attribute name="id">TestDiv</Attribute>
</Attributes>
<Class>display-label</Class>
<Value>PostCode</Value>
</Object>
</Page>
So does any one know how I could populate the xsl:element with the two attributes using xsl?
Thanks
Building from the stylesheet that I posted in the previous question, within the element declaration you can iterate through each of the Attributes/Attribute elements and construct the attributes for the element that you are constructing.
You are "standing" on the Object element node inside that for-loop, so you can then iterate over it's Attributes/Attribute elements like this:
<xsl:for-each select="Attributes/Attribute">
<xsl:attribute name="{#name}"><xsl:value-of select="current()"/></xsl:attribute>
</xsl:for-each>
Applied to your stylesheet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
</head>
<body>
<xsl:for-each select="Page/Object">
<xsl:element name="{Type}" >
<xsl:for-each select="Attributes/Attribute">
<xsl:attribute name="{#name}"><xsl:value-of select="current()"/></xsl:attribute>
</xsl:for-each>
<xsl:value-of select="Value"/>
</xsl:element>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
This is an alternative way to achieve the same output, but in a little more delcarative fashion, using apply-templates instead of for-each.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<html>
<head>
</head>
<body>
<xsl:apply-templates select="Page/Object" />
</body>
</html>
</xsl:template>
<xsl:template match="Object">
<xsl:element name="{Type}" >
<xsl:apply-templates select="Attributes/Attribute" />
<xsl:apply-templates select="Value" />
</xsl:element>
</xsl:template>
<xsl:template match="Attribute">
<xsl:attribute name="{#name}"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
You need to fix the Attributes element in your source sample, it's not closed.
You can use xsl:for-each or xsl:apply-templates with select="Attributes/Attribute", to invoke an xsl:attribute element that looks a bit like this:
<xsl:attribute name="{#name}"><xsl:value-of select="text()"/></xsl:attribute>
The thing you need to watch out for is that xsl:attribute must come before anything that adds children to the {Type} element.
<xsl:element name="Attribute">
<xsl:attribute name="class">TestStyle</xsl:attribute>
<xsl:attribute name="id">TestDiv</xsl:attribute>
</xsl:element>