I'm trying to conditionally display the content of HTML page depending if a document being generated for a recognised company or not.
However, the transformation doesn't work and I can't understand why :( I use MSXML3.0 as transformer and oXygen as IDE, which gives the errors I presented below.
What I do is to construct a long string of all recognised companies (default and extra if any). I then split them into <token> elements that are stored in the $companiesKnownList variable. To determine if a company is in that list I count how many times it occurs:
count($companiesKnownList/token[normalize-space(.) = $productName]) < 1
If it's less than 1, then the company doesn't appear in the $companiesKnownList variable and therefore is not recognized. Otherwise, if it appears in the $companiesKnownList variable once or more times it is a recognized company. Nevertheless, this is where it breaks and displays the following error:
Description: Code: 0x80004005
Description: The XSL processor stack has overflowed - probable cause is infinite template recursion.
Description: The transformer process ended with code: 1
I've noticed that if my XML has got a recognised company, ex #ProductName="ski" then transformation fails with stack overflow. If I have an unrecognized company, ex #ProductName="bla" the transformation works and text that it isn't a recognized company is displayed.
I don't understand what's going wrong with valid companies. I would be more than grateful if you could help me out. I have been staring at it for a day now...without any progress :S
Thanks!
Here is my stylesheet:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 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="msxsl str"
version="1.0">
<!-- Taken from http://www.exslt.org/str/functions/tokenize/index.html -->
<xsl:import href="str.tokenize.template.xsl"/>
<!-- normalize and lowcase product name -->
<xsl:variable name="productName"
select="normalize-space(translate(/Doc/#ProductName, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))"/>
<!-- default recognised companies for all docs -->
<xsl:variable name="defaultRecognisedCompanies" select="'ski, holiday, summer trips'"/>
<!-- Determine what companies to generate a doc for -->
<xsl:variable name="companiesKnownListRaw">
<xsl:call-template name="recognisedCompanies"/>
</xsl:variable>
<xsl:variable name="companiesKnownList" select="msxsl:node-set($companiesKnownListRaw)"/>
<!-- Lists recognised companies for a document to be generated for -->
<xsl:template name="recognisedCompanies">
<xsl:call-template name="recognisedCompaniesListForDocument"/>
</xsl:template>
<xsl:template name="recognisedCompaniesListForDocument">
<xsl:param name="defaultCompanies" select="$defaultRecognisedCompanies"/>
<xsl:param name="isUseDefaultsCompanies" select="true()"/>
<xsl:param name="extraCompanies" select="''"/>
<xsl:variable name="allCompaniesRaw">
<xsl:call-template name="str:tokenize">
<xsl:with-param name="string">
<xsl:choose>
<!-- keep default companies -->
<xsl:when test="$isUseDefaultsCompanies = 'true'">
<xsl:value-of select="concat($defaultCompanies, ', ', $extraCompanies)"/>
</xsl:when>
<!-- discard default companies -->
<xsl:otherwise>
<xsl:value-of select="$extraCompanies"/>
</xsl:otherwise>
</xsl:choose>
</xsl:with-param>
<xsl:with-param name="delimiters" select="','" />
</xsl:call-template>
</xsl:variable>
<!-- Normalize token's value and discard empty values -->
<xsl:for-each select="msxsl:node-set($allCompaniesRaw)/token">
<xsl:if test="normalize-space(.) != ''">
<token>
<xsl:value-of select="normalize-space(.)"/>
</token>
</xsl:if>
</xsl:for-each>
</xsl:template>
<!-- Construct HTML doc. Display appropriate message for a company if it's recognized or not -->
<xsl:output method="html" doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN"
doctype-system="http://www.w3.org/TR/html4/loose.dtd" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Doc">
<html>
<xsl:choose>
<!-- Not recognised company -->
<!-- There is something wrong with the count conditions, and I don't understand what :( -->
<xsl:when test="count($companiesKnownList/token[normalize-space(.) = $productName]) < 1">
<body>
<div align="center">
This type of company is NOT recognised for this document.
</div>
</body>
</xsl:when>
<!-- Recognised company -->
<xsl:otherwise>
<body>
<div align="center">
This type of company is recognised for this document.
</div>
</body>
</xsl:otherwise>
</xsl:choose>
</html>
</xsl:template>
</xsl:stylesheet>
XML is something simple like:
In this example, ski is recognized company, but transformation fails.
<?xml version="1.0" encoding="UTF-8"?>
<Doc ProductName="ski" />
In this example, bla is not a recognized company and transformation succeeds with displaying text: "This type of company is NOT recognised for this document."
<?xml version="1.0" encoding="UTF-8"?>
<Doc ProductName="bla" />
You need to add the implementation of your named template str:tokenize. Check Jeni Tennison implementation at http://www.exslt.org/str/functions/tokenize/str.tokenize.template.xsl
Then, add this as stylesheet top element, with correct href:
<xsl:include href="str.tokenize.template.xsl"/>
With that changes (and closing your last template) with this input:
<Doc ProductName="ski" />
Ouput:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<body>
<div align="center">
This type of company is recognised for this document.
</div>
</body>
</html>
MSXML (any version) does not support EXSLT -- and the XSLT processor produces an error message.
Could you, please, correct the question so that only true information is present?
Related
I have an .ods file and want to access the values of table-cells in table-rows by the value of the first column for the given row. So their heading in my case.
So the calc table looks like this:
First_Name | Last_Name
Peter | Parker
Emma | Stone
...
Here is my xslt-export-filter file:
SuperBasicExportFilter.xslt
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0"
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0"
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
exclude-result-prefixes="table text office"
>
<xsl:output
method="xml"
indent="yes"
encoding="UTF-8"
omit-xml-declaration="no"
/>
<xsl:template match="/">
<xsl:variable name="columnHeadings">
<xsl:for-each select="//table:table/table:table-row[not(preceding::table:table-row)]//table:table-cell">
<xsl:element name="heading">
<xsl:attribute name="name" select="text:p" />
<xsl:value-of select="position()" />
</xsl:element>
</xsl:for-each>
</xsl:variable>
<html>
<body>
<h1>Hello</h1>
<xsl:message>columnHeadings: <xsl:value-of select="$columnHeadings" /></xsl:message>
<table>
<xsl:for-each select="//table:table/table:table-row">
<xsl:if test="position() > 1">
<tr>
<td>
First Column Value
<xsl:value-of select="table:table-cell[1]/text:p" />
<!-- <xsl:value-of select="table:table-cell[$columnHeadings/heading[#name='First_Name']]/text:p" /> -->
</td>
<td>
Second Column Value
<xsl:value-of select="table:table-cell[2]/text:p" />
<!-- <xsl:value-of select="table:table-cell[$columnHeadings/heading[#name='Last_Name']]/text:p" /> -->
</td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The message shows "columnHeadings: 1234567891011121314" and so on. So it is setting the position values correctly.
I tried getting the values based on the "name" attribute on the "heading" element.
But I can't get the values individually in any way. It seems I can't use the $columnHeadings with any XPath expression. It just returns "Xpath evaluation returned no result".
I tried
wrapping the "heading" elements with a "columnHeadings" element inside the variable and setting the "as" value of the variable to "element()"
using the "node-set" function (after importing the "exslt" ns)
using <xsl:variable name="columnHeadingsNode" select="document('')//xsl:variable[#name = 'columnHeadings']" /> to then get the value
using the xsl:key element like <xsl:key name="columnHeadings" match="//table:table/table:table-row[not(preceding::table:table-row)]//table:table-cell" use="text:p" /> - but this way I can't access it based on the "name"
What other things can I try to access the variable contents with a xpath expression?
Is it even possible to access the values like table:table-cell[$columnHeadings/heading[#name='Last_Name']]?
Answers to comments:
Which XSLT processor are you using?
I'm using whatever libreoffice 7.4.5.1 is using.
Can I change that?
The xsl:vendor is "libxslt" and the version is "1.0" according to the <xsl:value-of select="system-property('xsl:vendor')"/> and xsl:version values.
Do you get an error on <xsl:attribute name="name" select="text:p" />?
I actually do not for some reason. The test runs through without errors. I get a new browser tab with the produced xml output and no errors.
I tried ticking the "The filter needs XSLT 2.0 processor" but then I can't test run the filter anymore and don't get any output.
What's the overall purpose of this exercise?
I want to be able to select the values in the columns by their respective column heading, instead of the index, because I want to make it as portable as possible. At least I think that would help to achieve that goal. I have 184 columns. The column names won't change as likely as the index of the column, I believe.
The intent <xsl:attribute name="name" select="text:p" /> fails to create an attribute with a value in XSLT 1; it should raise an error but it seems your XSLT processor kind of ignores the select.
So try
<xsl:attribute name="name">
<xsl:value-of select="text:p"/>
</xsl:attribute>
instead.
That way, I would think that e.g. <xsl:variable name="columnHeadings-ns" select="exsl:node-set($columnHeadings)" xmlns:exsl="http://exslt.org/common"/> should allow you to use e.g. <xsl:value-of select="table:table-cell[$columnHeadings-ns/heading[#name='First_Name']]/text:p"/>.
Source:
<Data>
<AB>
<choice>Disclose</choice>
<image>
<img alt="No Image" xlink:href="abcd:202-11587" xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:title="Image" />
</image>
<link>abcd</link>
</AB>
<AB>
<choice>All</choice>
<image>
<img alt="No Image" xlink:href="abcd:202-2202" xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:title="Image" />
</image>
<link>all</link>
</AB>
</Data>
XSLT
<xsl:template match="Data">
<xsl:for-each select="AB">
<xsl:variable name="temp" select="choice"/>
<xsl:choose>
<xsl:when test="$temp='Disclose'">
<xsl:apply-templates select="image/node()"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template match="simple:image/xhtml:img">
<!-- I want to get the the name of the "choice" here-->
<!-- some other process-->
<!-- how to access the value of the <choice> element of that section-->
<!-- how to access <link> element of that section-->
</xsl:template>
Can any one help how to do it.
Firstly, as this may just be an oversight with your code sample, you have specified namespaces in your matching template
<xsl:template match="simple:image/xhtml:img">
However, there are no references to the "simple" namespace in your sample XML, so in this case it should just be the following
<xsl:template match="image/xhtml:img">
But in answer to you question, to get the choice element, because you currently posisioned on the img element, you can search back up the hierarchy, like so
<xsl:value-of select="../../choice" />
The '..' represents the parent element. So, you are going back up to the AB element, and getting its child choice element.
And similarly for the link element
<xsl:value-of select="../../link" />
Note, it doesn't have to be xsl:value-of here, if there were multiple link elements, you could use xsl:apply-templates
<xsl:apply-templates select="../../link" />
And, if you required only link elements that occurred after the parent image element, you could do something like this
<xsl:apply-templates select="../following-sibling::link" />
I need help converting an XSLT 1.0 file to 2.0 (so I can use the XSLT 2.0 replace() function call).
I've Googled, searched different books and SO with no success. I tried changing version="1.0" to 2.0 and changing html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en" to simply html but this all just results in XSLTProcessor errors.
Any help is greatly appreciated. Thanks in advance.
Here is my XSLT:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml" version="1.0">
<xsl:output method="xml" omit-xml-declaration="yes" indent="no"
doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN" encoding="ISO-8859-1" />
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<xsl:element name="meta">
<xsl:attribute name="name">description</xsl:attribute>
<xsl:attribute name="content"><xsl:value-of select="EventList/title"/></xsl:attribute>
</xsl:element>
<xsl:element name="link"><xsl:attribute name="rel">alternate</xsl:attribute><xsl:attribute name="type">application/rss+xml</xsl:attribute><xsl:attribute name="title">RSS</xsl:attribute><xsl:attribute name="href"><xsl:value-of select="EventList/rssURL"/></xsl:attribute></xsl:element>
</head>
<body>
<xsl:apply-templates select="EventList" />
</body>
</html>
</xsl:template>
<xsl:template match="EventList">
<xsl:choose>
<xsl:when test="Event">
<xsl:apply-templates select="Event"/>
</xsl:when>
<xsl:otherwise>
<div class="eventItem">
<div class="eventItemText">
<p>There are currently no events posted for this category.</p>
</div>
</div>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- List -->
<xsl:template match="Event">
<li>
<!-- Title -->
<xsl:if test="eventStatus = 2"><xsl:value-of select="eventStatusString"/> - </xsl:if>
<xsl:element name="a"><xsl:attribute name="href">http://events.stanford.edu/e/e/?d=<xsl:value-of select="replace(detailpath,'/events/','')"/></xsl:attribute><xsl:attribute name="id"><xsl:value-of select="eventID"/></xsl:attribute><xsl:attribute name="rel">external</xsl:attribute>
<xsl:value-of select="title" disable-output-escaping="yes"/>
</xsl:element>
<!-- Date and time -->
<xsl:element name="a"><xsl:attribute name="href">http://events.stanford.edu/e/details.php?detailpath=<xsl:value-of select="detailpath"/></xsl:attribute><xsl:attribute name="rel">external</xsl:attribute>
<xsl:choose>
<xsl:when test="repeatRuleID > 0">
Ongoing <xsl:value-of select="repeatRuleText"/> from <xsl:value-of select="beginDate"/> through <xsl:value-of select="repeatUntilDate"/>.
<xsl:if test="repeatRuleID=99">See details for exact dates and times.</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="string(beginDay)"><xsl:value-of select="beginDay"/>, </xsl:if>
<xsl:value-of select="beginDate"/>.
</xsl:otherwise>
</xsl:choose>
<xsl:if test="repeatRuleID!=99">
<xsl:if test="string(beginTime)">
<xsl:text disable-output-escaping="yes"></xsl:text><xsl:value-of select="beginTime"/>.
</xsl:if>
</xsl:if>
</xsl:element>
<!-- Location -->
<xsl:element name="a"><xsl:attribute name="href">http://events.stanford.edu/e/details.php?detailpath=<xsl:value-of select="detailpath"/></xsl:attribute><xsl:attribute name="rel">external</xsl:attribute>
<xsl:value-of select="locationText"/>
</xsl:element>
</li>
</xsl:template>
</xsl:stylesheet>
In addition to changing the version attribute to '2.0', you need to feed your XSLT 2.0 code to an XSLT 2.0 processor.
At present, some of the XSLT 2.0 processors I use are:
Saxon 9.x
XQSharp 2.0
AltovaXML (XMLSpy)
Do note that an existing XSLT 1.0 code may behave differently under XSLT 2.0 -- the most obvious differences are that <xsl:value-of> no longer produces the string value of only the first node from a node-set, and that in XSLT 2.0 the dreaded RTF type has been eliminated, so no xxx:node-set() extension function is needed/provided.
You should also check the non normative XSLT 2.0 Appendix J Changes from XSLT 1.0 .
This section lists all known cases
where a stylesheet that was valid
(produced no errors) under XSLT 1.0,
and whose behavior was fully specified
by XSLT 1.0, will produce different
results under XSLT 2.0.
While you're converting the code, you should get rid of those ugly disable-output-escaping="yes" attributes. They are almost certainly not wanted. The use of this attribute usually indicates that it was written by a novice with a poor understanding of the language. This also applies to the use of verbose constructs like
<xsl:element name="a"><xsl:attribute name="href">http://events.stanford.edu/e/e/?d=<xsl:value-of select="replace(detailpath,'/events/','')"/></xsl:attribute><xsl:attribute name="id"><xsl:value-of select="eventID"/></xsl:attribute><xsl:attribute name="rel">external</xsl:attribute>
<xsl:value-of select="title" disable-output-escaping="yes"/>
</xsl:element>
that could be replaced by the much more readable
<a href="http://events.stanford.edu/e/e/?d={replace(detailpath,'/events/','')}"
id="{#eventID}" rel="external">
<xsl:value-of select="title"/>
</a>
Generally, some refactoring is long overdue for this code.
I am trying to basically recreate the functionality of an ASP.NET master page with an XSLT template.
I have a "master page" template that contains much of the page html stored in an .xslt file. I have another .xslt file specific to a single page, that takes in xml representing the page data. I want to call the master page template from my new template, and still have the ability to insert my own xml that will be applied. If I could pass a param that would allow me to call template with the param as the name, that would do the trick, but that doesn't appear to be allowed.
Basically I have this:
<xsl:template name="MainMasterPage">
<xsl:with-param name="Content1"/>
<html>
<!-- bunch of stuff here -->
<xsl:value-of select="$Content1"/>
</html>
</xsl:template>
And this:
<xsl:template match="/">
<xsl:call-template name="MainMasterPage">
<xsl:with-param name="Content1">
<h1>Title</h1>
<p>More Content</p>
<xsl:call-template name="SomeOtherTemplate"/>
</xsl:with-param>
</xsl-call-template>
</xsl:template>
What happens is that the nested xml is basically stripped and all that is inserted is "TitleMore Content"
The problem with the provided code is here:
<xsl:value-of select="$Content1"/>
This will output either the concatenation of all text-nodes descendents of the top node of $Content1 (if it contains a document) or the string value of its first element or text child (if it is an XML fragment).
You need to use
<xsl:copy-of select='$pContent1'>
instead of
<xsl:value-of select='$pContent1'>.
This correctly copies all children nodes of $pContent1
Below is a corrected transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:call-template name="MainMasterPage">
<xsl:with-param name="pContent1">
<h1>Title</h1>
<p>More Content</p>
<xsl:call-template name="SomeOtherTemplate"/>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="MainMasterPage">
<xsl:param name="pContent1"/>
<html>
<!-- bunch of stuff here -->
<xsl:copy-of select="$pContent1"/>
</html>
</xsl:template>
<xsl:template name="SomeOtherTemplate">
<h2>Hello, World!</h2>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted, correct result is produced:
<html>
<h1>Title</h1>
<p>More Content</p>
<h2>Hello, World!</h2>
</html>
I have the following XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<example>
<contactInfo>
<id>12319221</id>
<name>Jerry P</name>
<market>
<name>Test</name>
<phone>800.555.1010</phone>
</market>
<agent>
<name>Test User</name>
<email>testuser#email.com</email>
</agent>
<summary>“Jerry just gets it!”</summary>
</contactInfo>
</example>
I am encoding special characters as html entities when I save this xml document, hence how the smart quotes are encoded as “ and ”.
And I use an XSL, via Java/Xalan, to transform the xml document to html:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="wsHost"></xsl:param>
<xsl:param name="serverId"></xsl:param>
<xsl:template match="/showcase">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example</title>
</head>
<body>
<div id="profile">
<xsl:apply-templates/>
</div>
</body>
</html>
</xsl:template>
<!-- Contact Info section -->
<xsl:template match="/example/contactInfo">
<span class="sectionTitle">Contact Info:</span>
<div id="contactInfo">
<xsl:if test="name">
<strong>Candidate Name:</strong> <xsl:value-of disable-output-escaping="yes" select="name" /><br />
</xsl:if>
<xsl:if test="id">
<strong>Candidate ID:</strong> <xsl:value-of disable-output-escaping="yes" select="id" /><br />
</xsl:if>
<xsl:if test="market">
<xsl:if test="market/name">
<strong>Market Name:</strong> <xsl:value-of disable-output-escaping="yes" select="market/name" /><br />
</xsl:if>
<xsl:if test="market/phone">
<strong>Market Phone:</strong> <xsl:value-of disable-output-escaping="yes" select="market/phone" /><br />
</xsl:if>
</xsl:if>
<xsl:if test="agent">
<xsl:if test="agent/name">
<strong>Agent Name:</strong> <xsl:value-of disable-output-escaping="yes" select="agent/name" /><br />
</xsl:if>
<xsl:if test="agent/email">
<strong>Agent Email:</strong> <xsl:value-of disable-output-escaping="yes" select="agent/email" /><br />
</xsl:if>
</xsl:if>
<xsl:if test="summary">
<strong>Summary:</strong> <xsl:value-of disable-output-escaping="yes" select="summary" /><br />
</xsl:if>
</div>
<hr size="1" noshade="noshade" class="rule" />
</xsl:template>
</xsl:stylesheet>
The html that results from the transform is then written to the browser. Here is where I'm noticing a character encoding issue. The (nbsp numeric value) show up as either black diamond question marks (firefox) or a box character (ie) and so do the entities that were previously encoded (“ / ”).
Also, maybe the biggest hint of all is that when transforming this xml file on a linux platform (then writing html to firefox) everything appears correctly. It's only when the transform is done from windows do the character encoding issues occur (in both firefox and ie).
Am I encoding the entities incorrectly or maybe not specify a character set somewhere?
You say you are using Java/Xalan. Are you prividing the output stream or stream writer? If so you need to explicitly set the encoding at that point:
... new OutputStreamWriter(stream,"UTF-8");
Just including the UTF8 headers does not actually cause the output file to be UTF8 encoded.
Well you havent set the encodeing in the HTML document for one. Dont know if thats the issue but that would be my first attempt to fix.
try adding:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
to your head.