Get a specific processing instruction - xslt

I've the below XML.
<?xpp /MAIN?>
<?xpp MAIN;1;0;0;0;619;0;0?>
<section>
<title>Introduction</title>
<para>
para<superscript>1</superscript>
<?xpp foot;art6_ft1;suppress?>
<?xpp FOOT;art6_ft1;1?>
<footnote label="1" id="art6_ft1">
<para>
data
</para>
</footnote>
<?xpp /FOOT?>
The data
</para>
</section>
Here I want to get the processing instruction containing MAINin it, but i'm unable to know how to get it.
I'm trying the below XSLT.
<xsl:template match="/">
<html>
<head>
</head>
<body>
<xsl:if test="//footnote">
<xsl:apply-templates select="//processing-instruction('xpp')[not(ancestor::toc)]| //footnote" mode="footnote"/>
</xsl:if>
</body>
</html>
</xsl:template>
.
.
.
.
.
.
.
<xsl:template match="processing-instruction('xpp')" mode="footnote">
<xsl:if test="following::footnote[1][preceding::processing-instruction('xpp')[1] = current()]">
<xsl:variable name="pb" select="."/>
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="$pb"/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
</xsl:if>
</xsl:template>
running this i'm getting <?xpp FOOT;art6_ft1;1?> picked, but i want <?xpp MAIN;1;0;0;0;619;0;0?> to be picked, please let me know how can i do this.
Thanks

"Here I want to get the processing instruction containing MAIN in it, but i'm unable to know how to get it."
You can use the following XPath expression to match processing instruction named xpp having data contains text "MAIN" :
processing-instruction('xpp')[contains(.,'MAIN')]

Related

Change attribute value to position of another element with corresponding attribute value

I have a single XHTML document that contains span and div elements that refer to page breaks of a print version using id and epub:type attributes. For example: <div epub:type="pagebreak" id="page-3"/>. The document also has links to those elements, for example: 3.
This single XHTML document will be split into multiple XHTML documents to form an EPUB package. For this reason, the href attributes need to be updated to match the new location of the corresponding id. For example: 3. The name of the new XHTML file is equal to the position of the body/section elements. So in the last example, the page break with id="page-3" is apparently in the second body/section element.
I'm using the following XSLT 2.0 stylesheet:
<!--identity transform-->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--variable to match id of elements with pagebreak values-->
<xsl:variable name="page-id" select="//*[#epub:type = 'pagebreak']/#id"/>
<!--update href attributes to match new filenames-->
<xsl:template match="a/#href">
<xsl:choose>
<xsl:when test="tokenize(., '#')[last()] = $page-id">
<xsl:attribute name="href">
<xsl:number count="//body/section[$page-id = tokenize(., '#')[last()]]" format="01"/>
<xsl:value-of select="concat('.xhtml', .)"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
It checks for href attributes that have a corresponding id using the $page-id variable. If there is a match, the href attribute should be updated using the count() function. Otherwise, the href should remain unchanged. The test seems to work, however, I'm not getting the result I want. This is the input:
<body>
<section>
<p>Link to page 3: 3</p>
</section>
<section>
<div epub:type="pagebreak" id="page-3"/>
</section>
</body>
This is the output I get:
<body>
<section>
<p>Link to page 3: 3</p>
</section>
<section>
<div epub:type="pagebreak" id="page-3"/>
</section>
</body>
This is the output I want:
<body>
<section>
<p>Link to page 3: 3</p>
</section>
<section>
<div epub:type="pagebreak" id="page-3"/>
</section>
</body>
It seems as if the XPath expression within xsl:number doesn't return a result, but I can't figure out why. Can anyone help me with this please?
I think you want e.g.
<xsl:template match="body/section" mode="number">
<xsl:number format="01"/>
<xsl:template>
and then instead of
<xsl:number count="//body/section[$page-id = tokenize(., '#')[last()]]" format="01"/>
use
<xsl:apply-templates select="key('page-id', substring-after(., '#'))" mode="number"/>
plus a key declaration
<xsl:key name="page-id" match="body/section" use=".//*[#epub:type = 'pagebreak']/#id"/>

How to test XSLT having multiple mode with XSpec?

Need to write XSpec test case to test the XSLT, in which multiple modes are used for transformation.
But with below test-case, the xspec only tests the output with default mode applied.
I wonder if there is a way to test the final output of the transformation.
<!-- input.xml -->
<body>
<div>
<p class="Title"><span>My first title</span></p>
<p class="BodyText"><span style="font-weight:bold">AAAAAAA</span><span>2 Jan 2020</span></p>
</div>
</body>
<!-- conv.xsl -->
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<!-- default mode : adding text-align attribute where #class=Title -->
<xsl:template match="*[ancestor::body]">
<xsl:choose>
<xsl:when test="#class = 'Title'">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#* except #style"/>
<xsl:attribute name="text-align" select="'center'"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- bodytext mode : changing element name to <title> where p[#class=Title] -->
<xsl:template match="p[#class]" mode="bodytext">
<xsl:choose>
<xsl:when test="#class = 'Title'">
<title>
<xsl:copy-of select="#* except #class"/>
<xsl:apply-templates mode="bodytext"/>
</title>
</xsl:when>
<xsl:otherwise>
<para>
<xsl:apply-templates mode="bodytext"/>
</para>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="body">
<xsl:variable name="data">
<body>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</body>
</xsl:variable>
<xsl:apply-templates select="$data" mode="bodytext"/>
</xsl:template>
<xsl:template match="node() | #*" mode="#all">
<xsl:copy>
<xsl:apply-templates select="node() | #*" mode="#current"/>
</xsl:copy>
</xsl:template>
O\P for first <p>:
-- after default mode applied: <p class="Title" text-align="center">. [below xspec tests this o\p]
-- final: <title text-align="center">. [Want to test this o\p]
<!-- test.xspec -->
<x:description xmlns:x="http://www.jenitennison.com/xslt/xspec" stylesheet="conv.xsl">
<x:scenario label="XSS00001: Testing 'p[#class=Title]' converts to 'title'">
<x:context href="input.xml" select="/body/div[1]/p[1]"/>
<x:expect label="Testing 'p' converts to 'title'">
<title text-align="center">
<span>My first title</span>
</title>
</x:expect>
</x:scenario>
</x:description>
Any suggestion in this regard would be a great help. Thanks...
I don't think it is solely the use of the modes that doesn't give you the result you want. However, the way you have set up the modes in your XSLT, if you match on that /body/div[1]/p[1] in the XSpec test scenario, you will get the stylesheet applied to only that p element. And obviously for that p there is the match on *[ancestor::body] in the unnamed mode and processing stops in that mode as the other mode is never used from that template.
So you might need to make the body element the context and use a scenario like the following:
<x:scenario label="XSS00002: Testing 'p[#class=Title]' converts to 'title'">
<x:context>
<body>
<div>
<p class="Title">...</p>
<p class="BodyText">...</p>
</div>
</body>
</x:context>
<x:expect label="Testing 'p' converts to 'title'">
<body>
<div>
<title text-align="center">...</title>
<para>...</para>
</div>
</body>
</x:expect>
</x:scenario>
Martin is quite right.
Another way of writing would be:
<x:scenario label="When a document contains 'body//p[#class=Title]'">
<x:context href="input.xml" />
<x:expect label="'p' is converted to 'title[#text-align]'"
test="body/div/title">
<title text-align="center">
<span>My first title</span>
</title>
</x:expect>
</x:scenario>
that is,
Remove #select from x:context, because you and/or conv.xsl seem to assume the transformation to start always from the document node (/).
Add #test to x:expect, because you seem to be interested only in the title element in the transformation result.

check for the following node

I've a small question in XSLT. i need XPath to validate a condition. and below is my XML.
<root>
<para>
Erlanger and several associates formed a syndicate to acquire the lease of an island in the West Indies for £55,000. The idea was to mine the <page num="44"/>island for phosphates.
</para>
<para>
<content-style font-style="bold">2.25</content-style> A commission or payment that a promoter receives upon transfer of property to a company must also be disclosed.
<para>
board were all nominees of Green and Smith; <page num="45"/>accordingly, disclosure </para>
</para>
<para>
<content-style font-style="bold">2.26</content-style> If a promoter contracts with the company whether as vendor<footnote num="57" id="fn57">
<para>
<case>
<casename>
<content-style font-style="italic">Re Leeds & Hanley Theatres of Varieties Ltd</content-style>
</casename> [1902] Ch 809 (Court of Appeal, England)
</case>.
</para>
</footnote> or purchaser,<footnote num="58" id="fn58">
<para>
<case>
<casename>
<content-style font-style="italic">Habib Abdul Rahman v Abdul Cader</content-style>
</casename> (1886) 4 Ky 193 (High Court of the Straits Settlements)
</case>.
</para>
</footnote> the fact that he is a contractor must be disclosed.
</para>
</root>
and XSL is as below.
<?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"
xmlns:ntw="Number2Word.uri"
exclude-result-prefixes="ntw">
<xsl:output method="html"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="ThisDocument" select="document('')"/>
<xsl:template match="/">
<xsl:text disable-output-escaping="yes"><![CDATA[<!DOCTYPE html>]]></xsl:text>
<html>
<head>
<xsl:text disable-output-escaping="yes"><![CDATA[</meta>]]></xsl:text>
<title>
<xsl:value-of select="chapter/title[1]/*"/>
</title>
<link rel="stylesheet" href="C:\Users\u0138039\Desktop\Proview\SG\Commentary_SG_XML-03032014\SG-Business Guide to Competition Law\05192014\XSLT\main.css" type="text/css"/>
<xsl:text disable-output-escaping="yes"><![CDATA[</link>]]></xsl:text>
</head>
<body>
<xsl:apply-templates/>
<xsl:if test="//footnote">
<section class="tr_footnotes">
<hr/>
<xsl:apply-templates select="//page[not(ancestor::toc)]| //footnote" mode="footnote"/>
</section>
</xsl:if>
</body>
</html>
</xsl:template>
<xsl:template match="footnote">
<xsl:variable name="varHeaderNote" select='concat("f",#num)'/>
<xsl:variable name="varFootNote" select='concat("#ftn.",#num)'/>
<sup>
<a name="{$varHeaderNote}" href="{$varFootNote}" class="tr_ftn">
<xsl:value-of select="#num"/>
</a>
</sup>
</xsl:template>
<xsl:template match="page" mode="footnote">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="./#num"/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
</xsl:template>
<xsl:template match="footnote" mode="footnote">
<div class="tr_footnote">
<div class="footnote">
<sup>
<a>
<xsl:attribute name="name">
<xsl:text>ftn.</xsl:text>
<xsl:value-of select="#num"/>
</xsl:attribute>
<xsl:attribute name="href">
<xsl:text>#f</xsl:text>
<xsl:value-of select="#num"/>
</xsl:attribute>
<xsl:attribute name="class">
<xsl:text>tr_ftn</xsl:text>
</xsl:attribute>
<xsl:value-of select="#num"/>
</a>
</sup>
<xsl:apply-templates/>
</div>
</div>
</xsl:template>
</xsl:stylesheet>
here when i run this, i get both <?pb label='44'?><?pb label='45'?>
where as i need as condition as below.
there should only be a `footnote` following `page` and there should be no `page` between `page` and `footnote`
in simple, by taking the above example, there are two page, by ignoring all other tags and considering only page the structure looks like below.
page num='44'
page num='45'
footnote
here i want only page num='45' to be captured and leave page num='44' since page num='44' is followed by another page but not directly footnote, this is pretty confusing, please let me know how can i do this.
The demo can be found here
Thanks
To capture only pages that contain at least one footnote, you could use a test like
(following::page | following::footnote)[1][self::footnote]
i.e. take all the following page and footnote elements in document order, and check whether the first one of these elements is a footnote - if it isn't then either there's an intervening page or there are no more page or footnote elements at all, either way we know there are no footnotes on this page.
<xsl:template match="page[(following::page | following::footnote)[1][self::footnote]]" mode="footnote">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="./#num"/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
</xsl:template>
<xsl:template match="page" mode="footnote" />
in simple, by taking the above example, there are two page, by
ignoring all other tags and considering only page the structure
looks like below.
page num='44'
page num='45'
footnote
here i want only page num='45' to be captured and leave page
num='44' since page num='44' is followed by another page but not
directly footnote
To select pages that are immediately followed by a footnote, use:
page[following-sibling::*[1][self::footnote]]
If a footnote is always preceded by a page, you could also use:
footnote/preceding-sibling::page[1]
Edit:
In your real example, where pages and footnotes are not siblings, you should use Ian's answer, i.e :
page[(following::page | following::footnote)[1][self::footnote]]
or (assuming that there is only block of footnotes):
footnote[1]/preceding::page[1]
When you match a page you can check whether the next footnote has a preceding page which is the current page. If it's not, then you don't print out its processing instruction since it's a page without a footnote.
<xsl:template match="page" mode="footnote">
<xsl:if test="following::footnote[1][preceding::page[1]/#num = current()/#num]">
<xsl:processing-instruction name="pb">
<xsl:text>label='</xsl:text>
<xsl:value-of select="./#num"/>
<xsl:text>'</xsl:text>
<xsl:text>?</xsl:text>
</xsl:processing-instruction>
</xsl:if>
</xsl:template>
See: http://xsltransform.net/eiQZDbt/3

XSL processor stack has overflowed - can't understand why

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?

HTML entities do not render correctly in browser after XSLT transform

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>&#8220;Jerry just gets it!&#8221;</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 &#8221.
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.