I have the below piece of XML
<title>Conduct of external affairs <content-style font-style="italic">via</content-style> NGOs</title>
here when i'm trying to match with REGEX it is throwing me an error
The template i'm using is
<?xml version='1.0'?>
<xslt:stylesheet version="2.0" xmlns:xslt="http://www.w3.org/1999/XSL/Transform"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="Conjunction">(of)|(to)|(and)|(the)</xsl:param>
<xsl:template match="title">
<xsl:call-template name="changeUpperCase">
<xsl:with-param name="Text" select="text()"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="changeUpperCase">
<xsl:param name="Text"/>
<xsl:value-of select="for $EachToken in tokenize(lower-case($Text), ' ')
return
if(matches($EachToken, $Conjunction))
then
$EachToken
else
concat(upper-case(substring($EachToken, 1, 1)), substring($EachToken, 2))"/>
</xsl:template>
</xslt:stylesheet>
the error i'm getting is
XSLT 2.0 Debugging Error: Error: file:///C:/Users/u0138039/Desktop/Proview/HK/The%20Law%20of%20the%20Hong%20Kong%20Constitution/The%20Law%20of%20the%20Hong%20Kong%20Constitution/XSLT/Chapters.xsl:39: Wrong occurrence to match required sequence type - Details: - XPTY0004: The supplied sequence ('2' item(s)) has the wrong occurrence to match the sequence type xs:string ('zero or one')
i also declared a template for content-style, but i'm unable to know how to link it.
<xsl:template match="content-style">
<xsl:choose>
<xsl:when test="./#format">
<span class="{concat('format-',#format)}">
<xsl:apply-templates/>
</span>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="fontStyle">
<xsl:value-of select="concat('font-style-',#font-style)"/>
</xsl:variable>
<span class="{$fontStyle}">
<xsl:value-of select="."/>
<xsl:apply-templates select="para"/>
</span>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
but the above template is working if i use the below XML piece of code
<title>IS HONG KONG AN INTERNATIONAL PERSON?</title>
Case 2:
<title>PREPARATION FOR TRANSFER OF SOVEREIGNTY</title>
this should be converted to
Preperation for Transfer of Sovereignty
please let me know how i can solve this issue.
Thanks
I would suggest to use a consistent style of code with matching templates instead of called templates:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="Conjunction">(of)|(to)|(and)|(the)|(for)</xsl:param>
<xsl:template match="title">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="title/text()">
<xsl:value-of select="for $EachToken in tokenize(lower-case(.), ' ')
return
if(matches($EachToken, $Conjunction))
then
$EachToken
else
concat(upper-case(substring($EachToken, 1, 1)), substring($EachToken, 2))"/>
</xsl:template>
<xsl:template match="content-style">
<xsl:variable name="fontStyle" select="concat('font-style-',#font-style)"/>
<span class="{$fontStyle}">
<xsl:value-of select="."/>
<xsl:apply-templates select="para"/>
</span>
</xsl:template>
<xsl:template match="content-style[#format]">
<span class="{concat('format-',#format)}">
<xsl:apply-templates/>
</span>
</xsl:template>
</xsl:stylesheet>
That way the error simply vanishes as now there is a template for the text child nodes of your title elements processed by the apply-templates.
In your current code with-param name="Text" select="text()" you are passing a sequence of text child nodes to the template if the element has mixed content and that way the lower-case call has the wrong type of arguments.
The templates for content-style elements should now be applied as well but I am not sure they will do exactly what you want as you have not specified that in detail. Post a new question if there are still problems with that second part of your question.
Based on your comments I have revised the stylesheet to use analyze-string:
<?xml version='1.0'?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="Conjunction">^(of|to|and|the|for)$</xsl:param>
<xsl:template match="title">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="title/text()">
<xsl:analyze-string select="." regex="(\w)(\w*)">
<xsl:matching-substring>
<xsl:value-of
select="if (matches(., $Conjunction, 'i'))
then lower-case(.)
else concat(upper-case(regex-group(1)), lower-case(regex-group(2)))"/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
<xsl:template match="content-style">
<xsl:variable name="fontStyle" select="concat('font-style-',#font-style)"/>
<span class="{$fontStyle}">
<xsl:apply-templates/>
</span>
</xsl:template>
<xsl:template match="content-style[#format]">
<span class="{concat('format-',#format)}">
<xsl:apply-templates/>
</span>
</xsl:template>
</xsl:stylesheet>
Now Saxon 9.5 transforms
<root>
<title>HOW HIGH IS THE “HIGH DEGREE OF AUTONOMY’ OF HONG KONG?</title>
<title>PREPARATION FOR TRANSFER OF SOVEREIGNTY</title>
<title>Conduct of external affairs <content-style font-style="italic">via</content-style> NGOs</title>
</root>
into
<title>How High Is the “High Degree of Autonomy’ of Hong Kong?</title>
<title>Preparation for Transfer of Sovereignty</title>
<title>Conduct of External Affairs <span class="font-style-italic">via</span> Ngos</title>
Related
Please suggest, to generate range of numbers from two values.
I used call-template methods. Please advice. I am using XSLT 2.
XML:
<article>
<range>3-7</range>
</article>
XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="//range">
<xsl:variable name="var1" select="substring-before(., '-')"/>
<xsl:variable name="var2" select="substring-after(., '-')"/>
<range>
<xsl:attribute name="ID">
<xsl:call-template name="tmpPageRange">
<xsl:with-param name="stPage" select="$var1"/>
<xsl:with-param name="lstPage" select="$var2"/>
<xsl:with-param name="presentvalue" select="$var1"/>
</xsl:call-template>
</xsl:attribute>
<xsl:value-of select="."/>
</range>
</xsl:template>
<xsl:template name="tmpPageRange">
<xsl:param name="stPage"/>
<xsl:param name="lstPage"/>
<xsl:param name="presentvalue"/>
<xsl:if test="number($stPage) < number($lstPage)">
<xsl:value-of select="concat($presentvalue, ' ')"/>
<xsl:call-template name="tmpPageRange">
<xsl:with-param name="stPage" select="number($stPage) + 1"/>
<xsl:with-param name="lstPage"/>
<xsl:with-param name="presentvalue" select="$stPage"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Required OutPut:
<range ID="3 4 5 6 7">3-7</range>
You can use the following:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:template match="article/range">
<range>
<xsl:attribute name="ID">
<xsl:for-each select="xs:integer(tokenize(.,'-')[1]) to xs:integer(tokenize(.,'-')[2])">
<xsl:value-of select="."/>
<xsl:if test="position() != last()">
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:attribute>
<xsl:value-of select="."/>
</range>
</xsl:template>
</xsl:stylesheet>
Following on from Lingamurthy CS's answer, there are various shortcuts that XSLT 2.0 offers that allow you to shorten this substantially. In fact for your specific requirement you can roll it right the way up into an attribute value template:
<range ID="{xs:integer(tokenize(.,'-')[1]) to xs:integer(tokenize(.,'-')[2])}">
<xsl:value-of select="."/>
</range>
The XSLT 2.0 rule for converting a sequence of atomic values to a string in an AVT is to convert each item to a string individually and then output the resulting sequence separated by spaces. If you wanted a different separator (or no separator at all) then you could use xsl:attribute, which can take a separator attribute to override the default (single space) separator, e.g.
<range>
<xsl:attribute name="ID"
select="xs:integer(tokenize(.,'-')[1]) to xs:integer(tokenize(.,'-')[2])"
separator="," />
<xsl:value-of select="."/>
</range>
would produce <range ID="3,4,5,6,7">3-7</range>
I'm trying to write a recursive named template that will show the path of a given node:
<?xml version="1.0"?>
<testfile>
<section>
<title>My Section</title>
<para>Trying to write a recursive function that will return a basic xpath of a given node; in the case of this node, I would want to return testfile/section/para, I don't need /testfile/section[1]/para[1] or anything like that. The issue I'm having is that in the case of a named template, I don't know how to select a different node and apply it to the named template.</para>
</section>
</testfile>
I'm trying this template :
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- stylesheet to test a named template trying to build an xpath for a given node -->
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:call-template>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="ancestor::*">
<xsl:message><xsl:value-of select="name()"/> has a parent</xsl:message>
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name()"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
<!-- how to recursively call template with parent node? -->
</xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name()"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
As per the comment, I'm not sure how to apply a node other than the context node to the named template. The other strategy I tried was to send the node to the template as a param, but I don't know how(or if you can) apply an axis to a param, as in
$thisNode../*
etc.
I'm sure it's something simple that I'm missing...thanks.
You can indeed pass in the node as a param to the template....
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:param name="node" select="." />
To apply an axis to it, for example to test for ancestors, you would do this....
<xsl:when test="$node/ancestor::*">
And to pass its parent element to the template when you recursively call it, do this:
<xsl:with-param name="node" select="$node/parent::*" />
Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:call-template>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:param name="pathText"/>
<xsl:param name="node" select="." />
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="$node/ancestor::*">
<xsl:message><xsl:value-of select="name($node)"/> has a parent</xsl:message>
<xsl:call-template name="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name($node)"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
</xsl:with-param>
<xsl:with-param name="node" select="$node/parent::*" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name($node)"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
An alternate approach is to use xsl:apply-templates, but with the mode parameter to keep it separate from other template matches. Try this XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:apply-templates select="." mode="getXpath">
<xsl:with-param name="pathText" select="''"/>
</xsl:apply-templates>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*" mode="getXpath">
<xsl:param name="pathText"/>
<xsl:message>top of get xpath func path text : <xsl:value-of select="$pathText"/> </xsl:message>
<xsl:choose>
<xsl:when test="ancestor::*">
<xsl:message><xsl:value-of select="name()"/> has a parent</xsl:message>
<xsl:apply-templates select=".." mode="getXpath">
<xsl:with-param name="pathText">
<xsl:value-of select="name()"/> <xsl:text>/</xsl:text><xsl:value-of select="$pathText"/>
</xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:message><xsl:value-of select="name()"/> has no parent!</xsl:message>
<xsl:value-of select="$pathText"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
You shouldn't have to pass a node as a param if you just do an xsl:for-each.
Here's a modified example of your XSLT. (Notice that the positional predicates are only output in the path if they are needed.)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- stylesheet to test a named template trying to build an xpath for a given node -->
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<result>
<xsl:apply-templates/>
</result>
</xsl:template>
<xsl:template match="*">
<xsl:variable name="xpath">
<xsl:call-template name="getXpath"/>
</xsl:variable>
<element>element name : <xsl:value-of select="name()"/> path : <xsl:value-of select="$xpath"/></element>
<xsl:apply-templates/>
</xsl:template>
<xsl:template name="getXpath">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/',local-name())"/>
<!--Predicate is only output when needed.-->
<xsl:if test="(preceding-sibling::*|following-sibling::*)[local-name()=local-name(current())]">
<xsl:value-of select="concat('[',count(preceding-sibling::*[local-name()=local-name(current())])+1,']')"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Output (using the input from the question)
<result>
<element>element name : testfile path : /testfile</element>
<element>element name : section path : /testfile/section</element>
<element>element name : title path : /testfile/section/title</element>
<element>element name : para path : /testfile/section/para</element>
</result>
For what it's worth, I wrote a simple XPath generation template about a decade ago, in part 2 of my "styling stylesheets" article on DeveloperWorks:
Listing 4. Template that generates a Pseudo XPath in XSLT
<xsl:template name="pseudo-xpath-to-current-node">
<!-- Special-case for the root node, which otherwise
wouldn't generate any path at all. A bit of a kluge,
but it's simple and efficient. -->
<xsl:if test="not(parent::node())">
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:for-each select="ancestor-or-self::node()">
<xsl:choose>
<xsl:when test="not(parent::node())">
<!-- This clause recognizes the root node, which doesn't need
to be explicitly represented in the XPath. -->
</xsl:when>
<xsl:when test="self::text()">
<xsl:text>/text()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::comment()">
<xsl:text>/comment()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<xsl:text>/processing-instruction()[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::*">
<!-- This test for Elements works because the Principal
Node Type of the self:: axis happens to be Element.
-->
<xsl:text>/</xsl:text>
<xsl:value-of select="name(.)"/>
<xsl:text>[</xsl:text>
<xsl:number level="single"/>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:when test="self::node()[name()='xmlns' | starts-with(name(),'xmlns:')]">
<!-- This recognizes namespace nodes, though it's a bit
ugly. XSLT 1.0 doesn't seem to have a more elegant
test. XSLT 2.0 is expected to deprecate the whole
concept of namespace nodes, so it may become a moot
point.
NS nodes are unique; a count isn't required. -->
<xsl:text>/namespace::</xsl:text>
<xsl:value-of select="local-name(.)"/>
</xsl:when>
<xsl:otherwise>
<!-- If I've reached this clause, the node must be an
attribute. Attributes are unique; a count is not
required. -->
<xsl:text>/#</xsl:text>
<xsl:value-of select="name(.)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
That was an XSLT 1.0 solution, structured for clarity. It's probably possible to simplify it, especially if you're using XSLT and XPath 2.0.
As I explained there, this "pseudo-XPath" version ignores the namespace issue, since I didn't need it for that proof-of-concept tool and since it was intended for human-readable messages rather than for execution. It could be corrected to manage namespaces properly by changing it to write out paths that specify node type with a predicate explicitly testing localname and namespace URI. The resulting paths would be bulkier and harder for humans to process. Exercise for the reader, if you're so inclined.
You might also be able to replace the positional index with something more expressive... but knowing what's going to be meaningful is not easy.
Hope that helps. Have fun.
(Oh, almost forgot: I wouldn't be surprised if there are other solutions on the XSLT FAQ site.)
I think you want something like this:
<xsl:variable name="get.path">
<xsl:text> /</xsl:text>
<xsl:for-each select="ancestor-or-self::*">
<xsl:variable name="get.current.node" select="name(.)"/>
<xsl:value-of select="name()"/>
<xsl:text>[</xsl:text>
<xsl:value-of select="count(preceding-sibling::*[name(.) = $get.current.node]) + 1"/>
<xsl:text>]</xsl:text>
<xsl:if test="position() != last()">
<xsl:text>/</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
Namespace getting added to the inner element <i>,<b>, <mpval>. I want to get rid of this namespace.
My XML:
<Container xmlns="http://www.sss.org/schema/"
xmlns:meta="http://www.sss.org/schema/tangier/metadata">
<cs-properties>
My Parent level text 1
<mp>
text1 of first child <b> in bold</b>
<mpval>36-37</mpval>
text2 of child <i> in italic </i>
</mp>
My Parent level text2 in <i>italic</i> also in <b>bold </b>
</cs-properties>
</Container>
When I apply below XSL, I get namespace added to <i> element. Want to get rid of it.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:sub="http://www.sss.org/schema"
exclude-result-prefixes="xsl sub">
<xsl:variable name="ns" select="'http://www.sss.org/schema/'" />
<xsl:output indent="no" omit-xml-declaration="yes"/>
<xsl:variable name="inlineElements" select="'b','i','sub','sup'"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sub:cs-properties">
<!--<properties xmlns= "{$ns}">-->
<xsl:element name="cs-properties" namespace="{$ns}" >
<xsl:for-each-group select="node()" group-adjacent="self::text() or self::node()
[name()=$inlineElements]">
<xsl:choose>
<xsl:when test="current-grouping-key()=true()">
<parenttext>
<xsl:copy-of select="current-group()" copy-namespaces="no" />
</parenttext>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
<!--</properties>-->
</xsl:element>
</xsl:template>
<xsl:template match="sub:mp|sub:abs-max">
<xsl:element name="{name()}">
<xsl:for-each-group select="node()" group-adjacent="self::text() or self::node()
[name()=$inlineElements]">
<xsl:choose>
<xsl:when test="current-grouping-key()=true()">
<childtext>
<xsl:copy-of select="current-group()" copy-namespaces="no"/>
</childtext>
</xsl:when>
<xsl:otherwise>
<!--<xsl:apply-templates select="."/>-->
<xsl:copy-of select="current-group()" copy-namespaces="no"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result:
<Container xmlns="http://www.sss.org/schema"
xmlns:meta="http://www.sss.org/schema/tangier/metadata"><cs-properties><parenttext
xmlns="">
My Parent level text 1
</parenttext><mp xmlns=""><childtext>
text1 of first child <b xmlns="http://www.sss.org/schema"> in
bold</b></childtext><mpval xmlns="http://www.sss.org/schema">36-
37</mpval><childtext>
text2 of child <i xmlns="http://www.sss.org/schema"> in italic </i>
</childtext></mp><parenttext xmlns="">
My Parent level text2 in <i
xmlns="http://www.sss.org/schema">italic</i> also in <b
xmlns="http://www.sss.org/schema">bold </b></parenttext></cs-
properties></Container>
You can avoid the <parenttext xmlns=""> either by using <parenttext xmlns="http://www.sss.org/schema/"> in your markup or by putting xmlns="http://www.sss.org/schema/" on the styleheet's root element. The latter would affect all result elements which might be needed if you have them in more places in the stylesheet.
I need to write an XSLT function that transforms a sequence of nodes into a sequence of strings. What I need to do is to apply a function to all the nodes in the sequence and return a sequence as long as the original one.
This is the input document
<article id="4">
<author ref="#Guy1"/>
<author ref="#Guy2"/>
</article>
This is how the calling site:
<xsl:template match="article">
<xsl:text>Author for </xsl:text>
<xsl:value-of select="#id"/>
<xsl:variable name="names" select="func:author-names(.)"/>
<xsl:value-of select="string-join($names, ' and ')"/>
<xsl:value-of select="count($names)"/>
</xsl:function>
And this is the code of the function:
<xsl:function name="func:authors-names">
<xsl:param name="article"/>
<!-- HELP: this is where I call `func:format-name` on
each `$article/author` element -->
</xsl:function>
What should I use inside func:author-names? I tried using xsl:for-each but the result is a single node, not a sequence.
<xsl:sequence select="$article/author/func:format-name(.)"/> is one way, the other is <xsl:sequence select="for $a in $article/author return func:format-name($a)"/>.
I am not sure you would need the function of course, doing
<xsl:value-of select="author/func:format-name(.)" separator=" and "/>
in the template of article should do.
If only a sequence of #ref values should be generated there is no need for a function or xsl version 2.0.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:template match="article">
<xsl:apply-templates select="author" />
</xsl:template>
<xsl:template match="author">
<xsl:value-of select="#ref"/>
<xsl:if test="position() !=last()" >
<xsl:text>,</xsl:text>
</xsl:if>
</xsl:template>
</xsl:styleshee
This will generate:
#Guy1,#Guy2
Update:
Do have the string join by and and have a count of items. Try this:
<xsl:template match="article">
<xsl:text>Author for </xsl:text>
<xsl:value-of select="#id"/>
<xsl:apply-templates select="author" />
<xsl:value-of select="count(authr[#ref])"/>
</xsl:template>
<xsl:template match="author">
<xsl:value-of select="#ref"/>
<xsl:if test="position() !=last()" >
<xsl:text> and </xsl:text>
</xsl:if>
</xsl:template>
With this output:
Author for 4#Guy1 and #Guy20
I want to usw the XSLT replace function to replace words in a text with
<strong>word</strong>.
I wrote the following template:
<xsl:template name="make-bold">
<xsl:param name="text"/>
<xsl:param name="word"/>
<xsl:variable name="replacement">
<strong><xsl:value-of select="$word"/></strong>
</xsl:variable>
<xsl:value-of select="replace($text, $word, $replacement )" />
</xsl:template>
Unfortunately, and are not rendered, althoug the rest works.
Could anyone help me?
Best, Suidu
Well the replace function http://www.w3.org/TR/xpath-functions/#func-replace takes a string and returns a string. You seem to want to create an element node, not a simple string. In that case using analyze-string http://www.w3.org/TR/xslt20/#analyze-string instead of replace could help.
Here is a sample XSLT 2.0 stylesheet:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output method="html" indent="no"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#*, node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="text()" mode="wrap">
<xsl:with-param name="words" as="xs:string+" select="('foo', 'bar')"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="text()" mode="wrap">
<xsl:param name="words" as="xs:string+"/>
<xsl:param name="wrapper-name" as="xs:string" select="'strong'"/>
<xsl:analyze-string select="." regex="{string-join($words, '|')}">
<xsl:matching-substring>
<xsl:element name="{$wrapper-name}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
When you run that with an XSLT 2.0 processor like Saxon 9 against the following input sample
<html>
<body>
<p>This is an example with foo and bar words.</p>
</body>
</html>
the output is as follows:
<html>
<body>
<p>This is an example with <strong>foo</strong> and <strong>bar</strong> words.</p>
</body>
</html>
hmm is is because here it its the string value that is replaced, you might try to use the node set?
i cannot test as i dont use xslt 2.0 but you might try a recursive template ie
<xsl:template match="yourtextelement">
<xsl:call-template name="MaketextStrong">
</xsl:template>
<xsl:template name="MaketextStrong">
<xsl:param name="text" select="."/>
<xsl:choose>
<xsl:when test="contains($text, 'texttomakestrong')">
<xsl:value-of select="substring-before($text, 'texttomakestrong')"/>
<strong>texttomakestrong</strong>
<xsl:call-template name="break">
<xsl:with-param name="text" select="substring-after($text,
'texttomakestrong')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>