Facing some attribute node issue - xslt

I am having the following code on my full length XSLT, and, its creates some errors during transformation.
The error is XPTY0004: A sequence of more than one item is not allowed as the first argument of matches()
<xsl:template match="par[#class='tablecaption']" exclude-result-prefixes="html">
<p class="caption1">
<xsl:variable name="n2" select="./text()|./*/text()"/>
<xsl:attribute name="id">
<xsl:if test="matches($n2, '(Table)\s(\d+|[A-Z])(\.)(\d+)')">
<xsl:variable name="y2" select="replace($n2, '(Table)\s(\d+|[A-Z])(\.)(\d+)', '$4')"/>Tab<xsl:value-of select="normalize-space(substring($y2, 1, 2))"/></xsl:if>
</xsl:attribute>
<strong><xsl:apply-templates/></strong></p>
</xsl:template>
What is the wrong here? pls

Change your variable n2 to ./descendant::text()[1]
<xsl:template match="par[#class='tablecaption']" exclude-result-prefixes="html">
<p class="caption1">
<xsl:variable name="n2" select="./descendant::text()[1]"/>
<xsl:attribute name="id">
<xsl:if test="matches($n2, '(Table)\s(\d+|[A-Z])(\.)(\d+)')">
<xsl:variable name="y2" select="replace($n2, '(Table)\s(\d+|[A-Z])(\.)(\d+)', '$4')"/>Tab<xsl:value-of select="normalize-space(substring($y2, 1, 2))"/></xsl:if>
</xsl:attribute>
<strong><xsl:apply-templates/></strong></p>
</xsl:template>
with this input
<root>
<par class="tablecaption"> Table A.1 <bold>text2</bold></par>
</root>
it gets:
<root>
<p class="caption1" id="Tab1"><strong> Table A.1 <bold>text2</bold></strong></p>
</root>

Related

Inline element data not appearing where expected after XSL transform

With the following XML
<para>Refer to Table 3 and Figure <grphcref refid="apm00-02-02-000018" shownow="0">6</grphcref>
for the door dimensions and clearances.</para>
and this XSL:
<xsl:template match="prcitem">
<xsl:for-each select="para">
<p>
<xsl:value-of select="." />
<xsl:apply-templates select="./grphcref" />
</p>
</xsl:for-each>
<xsl:apply-templates select="grphcref" />
<xsl:apply-templates select="table" />
<xsl:apply-templates select="unlist" />
</xsl:template>
<xsl:template match="grphcref">
<xsl:variable name="gotoimg" select="concat('#',#refid)"/>
<a href="{$gotoimg}" >
<xsl:value-of select="." /> - <xsl:value-of select="#refid" /> </a>
</xsl:template>
I get:
<p>Refer to Table 3 and Figure 6 for the door dimensions and clearances.
6 - apm00-02-02-000018
when I expected:
<p>Refer to Table 3 and Figure 6 - apm00-02-02-000018
for the door dimensions and clearances.
Can anyone offer guidance as to where I went wrong?
thx
If a para element is supposed to be transformed to a p element then in my view the "natural" way in XSLT is a template
<xsl:template match="para">
<p>
<xsl:apply-templates/>
</p>
</xsl:template>
If any other elements need special treatment add a template e.g.
<xsl:template match="grphcref">
<xsl:variable name="gotoimg" select="concat('#',#refid)"/>
<a href="{$gotoimg}" >
<xsl:value-of select="." /> - <xsl:value-of select="#refid" /> </a>
</xsl:template>
Text nodes are copied through to the result by the built-in templates.
The input sample doesn't explain why you mix template matching and for-each and why or whether you need xsl:apply-templates with any particularly selected nodes; as long as the input order should be preserved a simple processing of all child nodes with <xsl:apply-templates/> should suffice.

Prevent <cite> tag appearing within RSS Feed

Im using doctype: XHTML Mobile Profile 1.2, XML version="1.0 and Content-Type "application/xhtml+xml"
Is it possible to disable or prevent <cite> tag appearing within RSS feed, Due to the fact that I keep getting this error on the Page itself.
error on line 24 at column 70: expected '>'
Below is a rendering of the page up to the first error.
I am using an external feed from another site, which is not mine to control or edit.
I am using an XSLT and ColdFusion file to read the external RSS file, and display it the way I want within my XSLT, I already have in place disable-output-escaping="yes" to prevent lose code showing up within the feed. My XSLT works when this tag is not there
I have tried to get around it, but no luck. Is it actually possible to do this?
CFM
<cfcontent type="application/xhtml+xml" reset="yes">
<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">
<cfhttp method="Get" url="http://www.animenewsnetwork.com/news/rss.xml">
<cfset xmlInput = CFHTTP.FileContent>
<cfset MyXslFile = Expandpath("animenewsrss.xsl")>
<cffile action="READ" variable="xslInput" file="#MyXslFile#">
<cfset xmlOutput = XMLTransform(xmlInput, xslInput)>
<cfoutput>#xmloutput#</cfoutput>
XSLT
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" />
<xsl:template match="rss/channel">
<xsl:element name="html">
<xsl:element name="head">
<xsl:element name="title">Anime News</xsl:element>
</xsl:element>
<xsl:element name="body">
<xsl:element name="div"><xsl:attribute name="id"><xsl:value-of select="'hstyle'"/></xsl:attribute>Media Events UK - Anime News</xsl:element>
<xsl:element name="div"><xsl:attribute name="id"><xsl:value-of select="'nstyle'"/>
</xsl:attribute><xsl:element name="a"><xsl:attribute name="href">index.cfm</xsl:attribute>Home</xsl:element> -
<xsl:element name="a"><xsl:attribute name="href">listings.cfm</xsl:attribute>Listings</xsl:element> -
<xsl:element name="a"><xsl:attribute name="href">venue.cfm</xsl:attribute>Venues</xsl:element>
</xsl:element>
<xsl:apply-templates select="item[position() < 6]" />
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="item[position() < 6]">
<div class="rsstyle">
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="link"/>
</xsl:attribute>
<xsl:value-of select="title" />
</xsl:element>
<xsl:element name="div">
<xsl:value-of select="pubDate" />
</xsl:element>
<xsl:element name="div">
<xsl:value-of select="concat(substring(description, 1, 50), '...')" disable-output-escaping="yes"/>
</xsl:element>
</div>
</xsl:template>
</xsl:stylesheet>
With this line
<xsl:value-of select="concat(substring(description, 1, 50), '...')" disable-output-escaping="yes"/>
you cut the content of <description>, which contains cite elements in some cases. This leads to lines like the following in your result HTML:
<div><cite>Ni No Kuni</cite>, <cite>Tales of Xillia</ci...</div>
As you can see, the cite element is not closed properly anymore, because you cut the content of the description element at 50 characters. If you counted the characters, you'd notice that the content of description stops at 50, then "..." is inserted.
If you describe your intent behind applying substring to decription elements, SO can help you find a good alternative to this.
My guess is that you need to take into account the possibility that description contains not only text, but also elements (like cite). Then, it makes sense to use substring only on the text content of description, like this:
concat(substring(description/text(),1,50),'...')
Then go on to catch the child elements of description, e.g. in a separate template:
<xsl:template match="cite[parent::description]">
<!--Deal with cite elements-->
</xsl:template>
EDIT: I adapted your stylesheet to process cite elements as children of description. There are 2 additional templates that process text nodes and cite nodes, both being children of description.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" omit-xml-declaration="yes" />
<xsl:template match="rss/channel">
<!--I left this template unchanged!-->
</xsl:template>
<xsl:template match="item[position() < 6]">
<div class="rsstyle">
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="link"/>
</xsl:attribute>
<xsl:value-of select="title" />
</xsl:element>
<xsl:element name="div">
<xsl:value-of select="pubDate" />
</xsl:element>
<xsl:element name="div">
<xsl:apply-templates select="description"/>
</xsl:element>
</div>
</xsl:template>
<xsl:template match="description">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()[parent::description]">
<xsl:copy/>
</xsl:template>
<xsl:template match="cite[parent::description]">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>

Generate a breadcrumb trail with xsl from a node structure

I have difficulties to write a template that generates a breadcrumb trial out of a node structure. It is not working correctly up to now, there is some flaw in my thinking how it should walk the item path.
Consider the following page structure:
<!-- ===== SITE PAGE STRUCTURE ===================================== -->
<index>
<item section="home" id="index"></item>
<item section="service" id="index">
<item id="content-management-systems">
<item id="p1-1"/>
<item id="p1-2"/>
<item id="p1-3"/>
</item>
<item id="online-stores"></item>
<item id="search-engines-and-ir"></item>
<item id="web-applications"></item>
</item>
<item section="solutions" id="index">
<item id="document-clustering"></item>
</item>
<item section="company" id="index">
<item section="company" id="about"></item>
<item section="company" id="philosophy" ></item>
...
</item>
...
</item>
This site index represents a site-structure of xml content pages in its hierarchy (consider it to be a menu). It contains of sections, that represent the site sections just as home, company, service, solutions, etc. These sections can contain sub-sections with pages, or just regular content pages. A content page (its xml contents such as title, text content, etc) is identified by the #id attribute in the item tree. The #id attribute mainly is used to fetch the content of the entire page that will be rendered to html.
The breadcrumb template uses the item node #id attribute to get the title of the page (which will be shown in the breadcrumb trail).
I try to implement the following template that walks the tree by checking the target section attribute #section and the target page attribute #id in the tree. I expect it to walk the axis down until the target item_target is found by comparing the ancestors #section attribute and the #id with $item_target of each node in that axis.
For example: Attribute *$item_section=service* and the page id *target item_target=p1-1* should now recursively "walk" to the section branch "service" (depth 1), check if the target page #id is found on this level. In this case it is not found, so it makes the next recurive call (via apply-templates) to the next item node level (in this case it would be content-management-systems, there the target item page p1-1 is found, so the trail process is finished:
The result should like this:
home >> service >> content management systems >> p1-1
But unfortunately it is not working correct, at least not in every case. Also maybe it can be solved more easily. I try to implement it as an recursive template that walks from the top (level 0) to the target page (item node) as a leaf.
<!-- walk item path to generate a breadcrumb trail -->
<xsl:template name="breadcrumb">
<a>
<xsl:attribute name="href">
<xsl:text>/</xsl:text>
<xsl:value-of select="$req-lg"/>
<xsl:text>/home/index</xsl:text>
</xsl:attribute>
<xsl:value-of select="'Home'"/>
</a>
<xsl:apply-templates select="$content/site/index" mode="Item-Path">
<xsl:with-param name="item_section" select="'service'"/>
<xsl:with-param name="item_target" select="'search-engines-and-ir'"/>
<xsl:with-param name="depth" select="0"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="item" mode="Item-Path">
<xsl:param name="item_section" />
<xsl:param name="item_target" />
<xsl:param name="depth" />
<!--
depth=<xsl:value-of select="$depth"/>
count=<xsl:value-of select="count(./node())"/><br/>
-->
<xsl:variable name="cur-id" select="#id"/>
<xsl:variable name="cur-section" select="#section"/>
<xsl:choose>
<xsl:when test="#id=$item_target">
>>
<a>
<xsl:attribute name="href">
<xsl:text>/</xsl:text>
<!-- req-lg: global langauge variable -->
<xsl:value-of select="$req-lg"/>
<xsl:text>/</xsl:text>
<xsl:value-of select="$item_section"/>
<xsl:text>/</xsl:text>
<xsl:if test="$depth = 2">
<xsl:value-of select="../#id"/>
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:value-of select="#id"/>
</xsl:attribute>
<xsl:value-of
select="$content/page[#id=$cur-id]/title"/>
</a>
</xsl:when>
<xsl:otherwise>
<xsl:if test="ancestor-or-self::item/#section = $item_section and count(./node()) > 0">
>>:
<a>
<xsl:attribute name="href">
<xsl:text>/</xsl:text>
<!-- req-lg: global langauge variable -->
<xsl:value-of select="$req-lg"/>
<xsl:text>/</xsl:text>
<xsl:value-of select="$item_section"/>
<xsl:text>/</xsl:text>
<xsl:if test="$depth = 2">
<xsl:value-of select="../#id"/>
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:value-of select="#id"/>
</xsl:attribute>
<xsl:value-of
select="$content/page[#id=$cur-id and #section=$item_section]/title"/>
</a>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select="item" mode="Item-Path">
<xsl:with-param name="item_section" select="$item_section"/>
<xsl:with-param name="item_target" select="$item_target"/>
<xsl:with-param name="depth" select="$depth + 1"/>
</xsl:apply-templates>
</xsl:template>
So as the hardcoded parameters in the template breadcrumb, target section = 'service' and target page = 'search-engines-and-ir', I expect an output like
home >> service >> search-engines-and-ir
But the output is
home >> service >> content-management-systems >> search-engines-and-ir
which is obviously not correct.
Can anybody give me a hint how to correct this issue? It would be even more elegant to avoid that depth checking, but up to now I cannot think of a other way, I am sure there is a more elegant solution.
I work with XSLT 1.0 (libxml via PHP5).
Hope my question is clear enough, if not, please ask :-) Thanks for the help in advance!
As simple as this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kNodeById" match="item" use="#id"/>
<xsl:template match="/">
<xsl:text>home</xsl:text>
<xsl:call-template name="findPath">
<xsl:with-param name="pStart" select="'service'"/>
<xsl:with-param name="pEnd" select="'search-engines-and-ir'"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="findPath">
<xsl:param name="pStart"/>
<xsl:param name="pEnd"/>
<xsl:for-each select=
"key('kNodeById', $pEnd)
[ancestor::item[#section=$pStart]]
[1]
/ancestor-or-self::item
[not(descendant::item[#section=$pStart])]
">
<xsl:value-of select=
"concat('>>', #id[not(../#section)], #section)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
the wanted, correct result is produced:
home>>service>>search-engines-and-ir
Do Note:
This solution prints the breadcrumb from any node -- anywhere in the hierarchy to any of its descendent nodes -- anywhere in the hierarchy. More precisely, for the first item (in document order) with id attribute equal to $pEnd, the breadcrumb is generated from its inner-most ancestor whose section attribute is equal to $pStart -- to that item element.
This solution should be much more efficient than any solution using //, because we are using a key to locate efficiently the "end" item element.
II. XSLT 2.0 solution:
Much shorter and easier -- an XPathe 2.0 single expression:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kNodeById" match="item" use="#id"/>
<xsl:template match="/">
<xsl:value-of select=
"string-join(
(
'home',
key('kNodeById', $pEnd)
[ancestor::item[#section=$pStart]]
[1]
/ancestor-or-self::item
[not(descendant::item[#section=$pStart])]
/(#id[not(../#section)], #section)[1]
),
'>>'
)
"/>
</xsl:template>
</xsl:stylesheet>
You can just iterate over the ancestor-or-self:: axis . This is easy to do without recursion. For example...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
<html><body>
<xsl:call-template name="bread-crumbs">
<xsl:with-param name="items" select="*/item" />
<xsl:with-param name="section" select="'service'" />
<xsl:with-param name="leaf" select="'p1-2'" />
</xsl:call-template>
</body></html>
</xsl:template>
<xsl:template name="bread-crumbs">
<xsl:param name="items" />
<xsl:param name="section" />
<xsl:param name="leaf" />
<xsl:value-of select="concat($section,'>>')" />
<xsl:for-each select="$items/self::item[#section=$section]//item[#id=$leaf]/
ancestor-or-self::item[not(#section)]">
<xsl:value-of select="#id" />
<xsl:if test="position() != last()">
<xsl:value-of select="'>>'" />
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
...on your sample input yields...
<html>
<body>service>>content-management-systems>>p1-2</body>
</html>
...which renders as...
service>>content-management-systems>>p1-2

How to check whether a node is present in the file or not

I have xml which could have sometimes node DSD_G22 but not always. SO if that node is present then get the value of the elements otherwise assign empty:
input 1 scenario:
<root>
<G_83>
<G_8301/>
<G_8302/>
</G_83>
<DSD_G22>
<DSD_G22_G2201>Value</DSD_G22_G2201>
<DSD_G22_G2202>Value1</DSD_G22_G2202>
</DSD_G22>
</root>
Scenario Input 2:
<root>
<G_83>
<G_8301/>
<G_8302/>
</G_83>
</root>
The output for scenario 2 should be:
<G_83>
<G_8301/>
<G_8302/>
</G_83>
<DSD_G22>
<DSD_G22_G2201/>
<DSD_G22_G2202/>
<DSD_G22/>
</root>
I tried to do this but doesn't work. Please HelP:
<xsl:variable name="emptySpace" select="' '" />
<xsl:if test="#DSD_G22">
<xsl:if test="#DSD_G22_G2201">
<xsl:attribute name="DSD_G22_G2201">
<xsl:value-of select="#DSD_G22_G2201" />
</xsl:attribute>
</xsl:if>
<xsl:if test="#DSD_G22_G2202">
<xsl:attribute name="DSD_G22_G2202">
<xsl:value-of select="#DSD_G22_G2202" />
</xsl:attribute>
</xsl:if>
</xsl:if>
<xsl:if test="not(#DSD_G22)">
<xsl:attribute name="DSD_G22_G2201">
<xsl:value-of select="#emptySpace " />
</xsl:attribute>
<xsl:attribute name="DSD_G22_G2202">
<xsl:value-of select="#emptySpace" />
</xsl:attribute>
</xls:if>
This would do it for you:
<DSD_G22>
<DSD_G22_G2201><xsl:value-of select="DSD_G22/DSD_G22_G2201"/></DSD_G22_G2201>
<DSD_G22_G2202><xsl:value-of select="DSD_G22/DSD_G22_G2202"/></DSD_G22_G2202>
</DSD_G22>
<xsl:if test="count(DSD_G22) > 0">
Exists!
</xsl:if>

Checking the starting character should be 'T' and next 3 character should be number in xslt

I am using XSLT1.0 for transforming my XML.
I am having below Tabs.xml
<?xml version="1.0"?>
<tcm:ListItems xmlns:tcm="http://www.tridion.com/ContentManager/5.0" ID="tcm:481-87289-4" Managed="68">
<tcm:Item ID="tcm:481-596728-64" Title="T000. Brisbane" pageURL="/australia/brisbane/index.aspx" componentTitle="Overview"/>
<tcm:Item ID="tcm:481-598671-64" Title="Tabs XML"/>
<tcm:Item ID="tcm:481-598672-64" Title="T030 Special Offers" pageURL="/australia/brisbane/specialoffers.aspx" componentTitle="Special Offers"/>
<tcm:Item ID="tcm:481-598673-64" Title="020 Flight Schedules" pageURL="/australia/brisbane/flightschedules.aspx" componentTitle="Flight Schedules"/>
<tcm:Item ID="tcm:481-598674-64" Title="T010 Guide" pageURL="/australia/brisbane/guide.aspx" componentTitle="Guide"/>
</tcm:ListItems>
And I am using below xslt to transform it!
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tcm="http://www.tridion.com/ContentManager/5.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:em="http://www.emirates.com/tridion/schemas" xmlns:tcmse="http://www.tridion.com/ContentManager/5.1/TcmScriptAssistant" exclude-result-prefixes="em xlink tcmse tcm">
<xsl:output method="xml" version="1.0" encoding="UTF-16" indent="yes"/>
<xsl:template match="tcm:ListItems">
<list type="Tabs">
<xsl:apply-templates select="tcm:Item">
<xsl:sort select="#Title" order="ascending"/>
</xsl:apply-templates>
</list>
</xsl:template>
<!-- add field values for each item-->
<xsl:template match="tcm:Item">
<xsl:if test="#componentTitle != ''">
<xsl:element name="tab">
<xsl:attribute name="id">
<xsl:value-of select="substring-after(#ID, '-')"/>
</xsl:attribute>
<xsl:attribute name="title">
<xsl:value-of select="#componentTitle"/>
</xsl:attribute>
<xsl:attribute name="url">
<xsl:value-of select="#pageURL"/>
</xsl:attribute>
</xsl:element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Previously, it was working fine, but now there is another change required, now I need those node to be rendered whose #Title starts with 'T' or 't' as well as next 3 character should be number for example in above xml "Flight Schedule" shouldn't come, I assume i just need to write one more and condition in <xsl:if test="#componentTitle != ''">,
Please suggest! how it can be done!
Here is the simplest expression that will do the matching:
translate(substring(#Title, 1, 4), 't12345679', 'T000000000') = 'T000'
Here is the template:
<xsl:template match="
tcm:Item[translate(substring(#Title, 1, 4), 't12345679', 'T000000000') = 'T000'
and #componentTitle != '' "/>
The selection logic is best put into the match pattern of the template -- and it can be a little simplified, too.
<xsl:template match=
"tcm:Item[contains('tT', substring(#Title,1,1))
and
number(substring(#Title,2,3))
=
number(substring(#Title,2,3))
]
">
One rule to remember: Always test if some string $s represents a number -- like this:
number($s) = number($s)
You should select the nodes you want to operate on in your templates in the apply-templates element:
<xsl:apply-templates select="tcm:Item[#componentTitle != ''
and (starts-with(translate(#Title, 't', 'T'), 'T')
and string(number(substring(#Title,2,3))) != 'NaN')]">
I have used the test that #solution produced for this - it works well for the requirement (tested here).
This is better than selecting all tcm:Item nodes and testing them one at a time in the template.
Working with XSLT only very occasionally, I came up with the following solution:
<xsl:if test="#componentTitle != ''">
<xsl:if test="starts-with(#Title,'T') or starts-with(#Title,'t')">
<xsl:if test="string-length(#Title) > 3">
<xsl:if test="string(number(substring(#Title,2,3))) != 'NaN'">
...
</xsl:if>
</xsl:if>
</xsl:if>
</xsl:if>
I noticed that there are also functions lower-case/upper-case which could be used for testing for the first character being a t or T but they seem to be only available in XSLT2.0 .
I came up with below solution, please suggest if it is OK!
<xsl:template match="tcm:Item">
<xsl:if test="#componentTitle != '' and (starts-with(translate(#Title, 't', 'T'), 'T')and string(number(substring(#Title,2,3))) != 'NaN')">
<xsl:element name="tab">
<xsl:attribute name="id">
<xsl:value-of select="substring-after(#ID, '-')"/>
</xsl:attribute>
<xsl:attribute name="title">
<xsl:value-of select="#componentTitle"/>
</xsl:attribute>
<xsl:attribute name="url">
<xsl:value-of select="#pageURL"/>
</xsl:attribute>
</xsl:element>
</xsl:if>
</xsl:template>
Your all input is required!