I want to select first space of a string in a text.
Input :
<p>Text 1<italic>should</italic> Text 2.</p>
There is a space after </italic>. I want to select only that space and replace a <s> for that space. How can i do that.
Tried code :
<xsl:template match="p/text()[2]">
<s/>
</xsl:template>
Expected results :
<p type="body">Text 1
<style type="underline">should</style><s/>have surgery.</p>
This tried code not works properly. I am using xslt 2.0
In XSLT 2.0, you could do:
<xsl:template match="p/text()[preceding-sibling::*[1][self::italic]]">
<xsl:analyze-string select="." regex="^\s" >
<xsl:matching-substring>
<s/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="." />
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
This matches the text node immediately following an italic element, and checks if the first character is a space. If it is, it will be replaced by an empty s element.
Alternatively, you could restrict the match pattern itself to include only nodes that start with a space:
<xsl:template match="p/text()[preceding-sibling::*[1][self::italic]][starts-with(., ' ')]">
<s/>
<xsl:value-of select="substring(., 2)" />
</xsl:template>
Related
I have one question regarding xslt. In my input I have strings like:
<dxrCardNumber2>[EBERG615] [104699] [104913]</dxrCardNumber2>
and I have to remove all brackets and values between the brackets, if they contains a letter.
The result should be:
=> <dxrCardNumber2>104699 104913</dxrCardNumber2>
The position of the data I have to remove is random. I tried it with tokenize and then I can filter the not relevant entries out. But at teh end I have the problem to combine all entries again in one string.
Assuming you can use XSLT 2.0 or higher, you could do:
<xsl:template match="dxrCardNumber2">
<xsl:copy>
<xsl:value-of select="tokenize(translate(., '[]', ''), ' ')[matches(., '^\d+$')]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="dxrCardNumber2">
<xsl:analyze-string select="." regex="\[([A-Z0-9]+)\]\s+\[([0-9]+)\]\s+\[([0-9]+)\]" flags="i">
<xsl:matching-substring>
<dxrCardNumber2><xsl:value-of select="concat(regex-group(2),' ')"/>
<xsl:value-of select="regex-group(3)"/></dxrCardNumber2>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
I'm trying to replace a single char with an element (containing more elements).
Using XSL 2.0.
Example:
<element1>
<element2>some text and the char - I want to replace </element2>
...
</element1>
The - (dash) should now be replaced with a new element:
<element1>
<element2>some text and the char <newElement/> I want to replace </element2>
...
</element1>
I tried already:
<xsl:template match="element1">
<xsl:analyze-string select="." regex="-">
<xsl:matching-substring>
<newElement/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
But this removed all the other elements inbetween (because only strings are "returned").
And with the function replace() you only can insert strings (no < possible).
Any further ideas?
Your template matches an element(), but replaces text(). If you match text() and replace text() instead while copying the rest, it will work as expected:
<!-- modified identity template matching no text() nodes -->
<xsl:template match="element() | comment() | processing-instruction()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:analyze-string select="." regex="-">
<xsl:matching-substring>
<newElement/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:copy-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
Two corrections are needed:
Your template should match element2, not element1.
At the beginning and end of your tempate you should add
opening / closing tag for element2 (something like in
the identity template).
So your template should look like this:
<xsl:template match="element2">
<element2>
<xsl:analyze-string select="." regex="-">
<xsl:matching-substring>
<newElement/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</element2>
</xsl:template>
Of course, your script should include also the identity template.
I have a xml like this,
<doc>
<p>Biological<sub>89</sub> bases<sub>4456</sub> for<sub>8910</sub> sexual<sub>4456</sub>
differences<sub>8910</sub> in<sub>4456</sub> the brain exist in a wide range of
vertebrate species, including chickens<sub>8910</sub> Recently<sub>8910</sub> the
dogma<sub>8910</sub> of<sub>4456</sub> hormonal dependence for the sexual
differentiation of the brain has been challenged.</p>
</doc>
As you can see there are <sub> nodes and text() node contains inside the <p> node. and every <sub> node end, there is a text node, starting with a space. (eg: <sub>89</sub> bases : here before 'bases' text appear there is a space exists.) I need to replace those specific spaces with nodes.
SO the expected output should look like this,
<doc>
<p>Biological<sub>89</sub><s/>bases<sub>4456</sub><s/>for<sub>8910</sub><s/>sexual<sub>4456</sub>
<s/>differences<sub>8910</sub><s/>in<sub>4456</sub><s/>the brain exist in a wide range of
vertebrate species, including chickens<sub>8910</sub><s/>Recently<sub>8910</sub><s/>the
dogma<sub>8910</sub><s/>of<sub>4456</sub><s/>hormonal dependence for the sexual
differentiation of the brain has been challenged.</p>
</doc>
to do this I can use regular expression like this,
<xsl:template match="p/text()">
<xsl:analyze-string select="." regex="( )">
<xsl:matching-substring>
<xsl:choose>
<xsl:when test="regex-group(1)">
<s/>
</xsl:when>
</xsl:choose>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
But this adds <s/> nodes to every spaces in the text() node. But I only need thi add nodes to that specific spaces.
Can anyone suggest me a method how can I do this..
If you only want to match text nodes that start with a space and are preceded by a sub element, you can put the condition in your template match
<xsl:template match="p/text()[substring(., 1, 1) = ' '][preceding-sibling::node()[1][self::sub]]">
And if you just want to remove the space at the start of the string, a simple replace will do.
<xsl:value-of select="replace(., '^\s+', '')" />
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="no" />
<xsl:template match="p/text()[substring(., 1, 1) = ' '][preceding-sibling::node()[1][self::sub]]">
<s />
<xsl:value-of select="replace(., '^\s+', '')" />
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Just change the regex like so ^( ): it will match only the spaces at the beginning of the text part.
With this XSL snipped:
<xsl:analyze-string select="." regex="^( )">
Here is the result I obtain:
<p>Biological<sub>89</sub><s></s>bases<sub>4456</sub><s></s>for<sub>8910</sub><s></s>sexual<sub>4456</sub>
differences<sub>8910</sub><s></s>in<sub>4456</sub><s></s>the brain exist in a wide range of
vertebrate species, including chickens<sub>8910</sub><s></s>Recently<sub>8910</sub><s></s>the
dogma<sub>8910</sub><s></s>of<sub>4456</sub><s></s>hormonal dependence for the sexual
differentiation of the brain has been challenged.
</p>
I'd like to trim the leading whitespace inside p tags in XML, so this:
<p> Hey, <em>italics</em> and <em>italics</em>!</p>
Becomes this:
<p>Hey, <em>italics</em> and <em>italics</em>!</p>
(Trimming trailing whitespace won't hurt, but it's not mandatory.)
Now, I know normalize-whitespace() is supposed to do this, but if I try to apply it to the text nodes..
<xsl:template match="text()">
<xsl:text>[</xsl:text>
<xsl:value-of select="normalize-space(.)"/>
<xsl:text>]</xsl:text>
</xsl:template>
...it's applied to each text node (in brackets) individually and sucks them dry:
[Hey,]<em>[italics]</em>[and]<em>[italics]</em>[!]
My XSLT looks basically like this:
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
So is there any way I can let apply-templates complete and then run normalize-space on the output, which should do the right thing?
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p//text()[1][generate-id()=
generate-id(ancestor::p[1]
/descendant::text()[1])]">
<xsl:variable name="vFirstNotSpace"
select="substring(normalize-space(),1,1)"/>
<xsl:value-of select="concat($vFirstNotSpace,
substring-after(.,$vFirstNotSpace))"/>
</xsl:template>
</xsl:stylesheet>
Output:
<p>Hey, <em>italics</em> and <em>italics</em>!</p>
Edit 2: Better expression (now only three function calls).
Edit 3: Matching the first descendant text node (not just the first node if it's a text node). Thanks to #Dimitre's comment.
Now, with this input:
<p><b> Hey, </b><em>italics</em> and <em>italics</em>!</p>
Output:
<p><b>Hey, </b><em>italics</em> and <em>italics</em>!</p>
I would do something like this:
<xsl:template match="p">
<xsl:apply-templates/>
</xsl:template>
<!-- strip leading whitespace -->
<xsl:template match="p/node()[1][self::text()]">
<xsl:call-template name="left-trim">
<xsl:with-param name="s" value="."/>
</xsl:call-template>
</xsl:template>
This will strip left space from the initial node child of a <p> element, if it is a text node. It will not strip space from the first text node child, if it is not the first node child. E.g. in
<p><em>Hey</em> there</p>
I intentionally avoid stripping the space from the front of 'there', because that would make the words run together when rendered in a browser. If you did want to strip that space, change the match pattern to
match="p/text()[1]"
If you also want to strip trailing whitespace, as your title possibly implies, add these two templates:
<!-- strip trailing whitespace -->
<xsl:template match="p/node()[last()][self::text()]">
<xsl:call-template name="right-trim">
<xsl:with-param name="s" value="."/>
</xsl:call-template>
</xsl:template>
<!-- strip leading/trailing whitespace on sole text node -->
<xsl:template match="p/node()[position() = 1 and
position() = last()][self::text()]"
priority="2">
<xsl:value-of select="normalize-space(.)"/>
</xsl:template>
The definitions of the left-trim and right-trim templates are at Trim Template for XSLT (untested). They might be slow for documents with lots of <p>s. If you can use XSLT 2.0, you can replace the call-templates with
<xsl:value-of select="replace(.,'^\s+','')" />
and
<xsl:value-of select="replace(.,'\s+$','')" />
(Thanks to Priscilla Walmsley.)
You want:
<xsl:template match="text()">
<xsl:value-of select=
"substring(
substring(normalize-space(concat('[',.,']')),2),
1,
string-length(.)
)"/>
</xsl:template>
This wraps the string in "[]", then performs normalize-string(), then finally removes the wrapping characters.
I am rendering a list of tickers to html via xslt and I would like for the list to be comma deliimited. Assuming I was going to use xsl:for-each...
<xsl:for-each select="/Tickers/Ticker">
<xsl:value-of select="TickerSymbol"/>,
</xsl:for-each>
What is the best way to get rid of the trailing comma? Is there something better than xsl:for-each?
<xsl:for-each select="/Tickers/Ticker">
<xsl:if test="position() > 1">, </xsl:if>
<xsl:value-of select="TickerSymbol"/>
</xsl:for-each>
In XSLT 2.0 you could do it (without a for-each) using the string-join function:
<xsl:value-of select="string-join(/Tickers/Ticker, ',')"/>
In XSLT 1.0, another alternative to using xsl:for-each would be to use xsl:apply-templates
<xsl:template match="/">
<!-- Output first element without a preceding comma -->
<xsl:apply-templates select="/Tickers/Ticker[position()=1]" />
<!-- Output subsequent elements with a preceding comma -->
<xsl:apply-templates select="/Tickers/Ticker[position()>1]">
<xsl:with-param name="separator">,</xsl:with-param>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Ticker">
<xsl:param name="separator" />
<xsl:value-of select="$separator" /><xsl:value-of select="TickerSymbol" />
</xsl:template>
I know you said xsl 2.0 is not an option and it has been a long time since the question was asked, but for all those searching for a posibility to do what you wanted to achieve:
There is an easier way in xsl 2.0 or higher
<xsl:value-of separator=", " select="/Tickers/Ticker/TickerSymbol" />
This will read your /Tickers/Ticker elements and insert ', ' as separator where needed
If there is an easier way to do this I am looking forward for advice
Regards Kevin