How to match the case and apply templates - xpath

I've the below XML line of code.
<title><page>651</page>CHAPTER 13 This is <content-style font-style="italic">This goes in content-style</content-style> The title</title>
Here i'm trying to do the below.
Get the number after CHAPTER and concat it with Chapter, i'm able to do it with the below code.
<xsl:value-of select="concat('Chapter ', substring-before(substring-after(child::title,' '),' ')"/>
ignore the page in the title, and i use the below template match and able to do it.
<xsl:template match="title/page"/>
If there is just plain data, i'm using the below to get it.
<xsl:value of select ="substring-after(substring-after(./title,' '),' ')">
But the problem came in the above type, here i need to apply templates on substring-after(substring-after(.,' '),' ') and unfortunately this is not working.
I have the below XSLT
<xsl:template match ="title">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="content-style">
<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:template>
expected O/P
<div class="chapter-title">
<span class="chapter-num">Chapter 13</span><br /><br /> This is <span class="font-style-italic">This goes in content-style</span> the title</span></div>
Can you please let me know how can i do this.
Thanks

Your expected output has an unmatched span end tag.
Ignoring that, I would do something like:
<xsl:template match ="title">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="content-style">
<span class="font-style-{#font-style}">
<xsl:value-of select="."/>
</span>
</xsl:template>
<xsl:template match="title/text()">
<xsl:analyze-string select="." regex="Chapter [0-9]+">
<xsl:matching-substring>
<span class="chapter-num">
<xsl:value-of select="."/>
</span><br/>>br/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:-nonmatching-substring>
</xsl:analyze-string>
</xsl:template>

Related

differentiate scenarios using XPATH

I've the below cases.
**Case1:**
<para>1A/66</para>
<para>1A/34S/4</para>
<para>1/66</para>
**Case 2:**
<para>A/66</para>
<para>A1/1</para>
Here, the explaination is if the para starts with a letter (here it is A, it can be any alphabet), it should print case 2 else it should print case 1.
please let me know how can i do this.
Here is a DEmo
With XSLT 2.0 you have regular expression support so using e.g.
<xsl:template match="para[matches(., '^[a-zA-Z]')]">case 2</xsl:template>
<xsl:template match="para[matches(., '^[^a-zA-Z]')]">case 1</xsl:template>
Of course you can use a different regular expression to match on non-ASCII letters as well if "can be any alphabet" is meant to indicate other letters.
Here you are :
<xsl:template match="/">
<hmtl>
<head>
<title>New Version!</title>
</head>
<body>
<xsl:call-template name="print-param" />
<xsl:apply-templates select="child::*"/>
</body>
</hmtl>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match = "body">
<!-- Match cases -->
</xsl:template>
<xsl:template name = "print-param">
<!-- Match cases -->
**Case1:**
<xsl:for-each select=".//para[matches(., '^[A-Za-z]\.*')]">
<para><xsl:value-of select="." /></para>
</xsl:for-each>
**Case 2:**
<xsl:for-each select=".//para[not(matches(., '^[A-Za-z]\.*'))]">
<para><xsl:value-of select="." /></para>
</xsl:for-each>
</xsl:template>
</xsl:transform>
Check this demo

Sorting xslt nodes based on sibling

I am having some trouble getting this to work and I could really need some help.
I want to store the smallest variable that has a sibling which is not null.
I have a XML which looks like this:
<Root>
<Item>
<ValueOne>5</ValueOne>
<Item>bababa</Item>
</Item>
<Item>
<ValueOne>3</ValueOne>
<Item>ababa</Item>
</Item>
<Item>
<ValueOne>1</ValueOne>
<Item/>
</Item>
</Root>
I want the smallest ValueOne who's sibling Item has a value.
I was thinking something like:
<xsl:variable name="var">
<xsl:for-each select="Root/Item">
<xsl:if test="not(/Item = '')">
<xsl:sort data-type="text"/>
<xsl:if test="position()=1">
<xsl:value-of select="/Item"/>
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:variable>
But this doesn't work and I am not sure why. As you might tell I'm not the best at xslt. I cannot rename anything in the xml or change the structure of it.
Assuming those spaces and returns are non-essential, you could use:
<xsl:variable name="var">
<xsl:for-each select="Root/Item[normalize-space(Item)]">
<xsl:sort select="ValueOne" data-type="number" order="ascending"/>
<xsl:if test="position()=1">
<xsl:value-of select="normalize-space(ValueOne)"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
With all the redundant white space removed, this can be simplified to:
<xsl:variable name="var">
<xsl:for-each select="Root/Item[Item/text()]">
<xsl:sort select="ValueOne" data-type="number" order="ascending"/>
<xsl:if test="position()=1">
<xsl:value-of select="ValueOne"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>

Get full XPath from partial

I am using selenium with perl and have label on page, to access this label i have following xpath: //*[text()='some here'] , the problem that a need to get full xpath of this element, like /html/body/table/tr/..../any other/and other/ , is there is any selenium method or perl function ? looking for perl solution or any other working things.
thanks
looking for perl solution or any other
working things
This XPath 2.0 expression:
string-join(for $node in ancestor-or-self::node()
return concat(('#')[$node/self::attribute()],
$node/name(),
(concat('[',
count($node/preceding-sibling::node()
[name()=$node/name()]) + 1,
']'))[$node/../node()
[name()=$node/name()][2]]),
'/')
Edit: Shorter expression.
This XSLT 1.0 transformation produces an XPath expression for every node contained in the $pNode parameter:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<path>
<xsl:call-template name="buildPath"/>
</path>
<xsl:apply-templates select="node()|#*"/>
</xsl:template>
<xsl:template name="buildPath">
<xsl:variable name="pNode" select="."/>
<xsl:variable name="theResult">
<xsl:for-each select="$pNode">
<xsl:variable name="theNode" select="."/>
<xsl:for-each select=
"$theNode
|
$theNode/ancestor-or-self::node()[..]">
<xsl:element name="slash">/</xsl:element>
<xsl:choose>
<xsl:when test="self::*">
<xsl:element name="nodeName">
<xsl:value-of select="name()"/>
<xsl:variable name="thisPosition" select=
"count(preceding-sibling::*
[name(current())
=
name()])"/>
<xsl:variable name="numFollowing" select=
"count(following-sibling::
*[name(current())
=
name()])"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select=
"concat('[', $thisPosition +1, ']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:otherwise> <!-- This node is not an element -->
<xsl:choose>
<xsl:when test="count(. | ../#*) = count(../#*)">
<!-- Attribute -->
<xsl:element name="nodeName">
<xsl:value-of select="concat('#',name())"/>
</xsl:element>
</xsl:when>
<xsl:when test="self::text()"> <!-- Text -->
<xsl:element name="nodeName">
<xsl:value-of select="'text()'"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::text())"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::text())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select=
"concat('[', $thisPosition +1, ']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<!-- Processing Instruction -->
<xsl:element name="nodeName">
<xsl:value-of select="'processing-instruction()'"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::processing-instruction())"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::processing-instruction())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select=
"concat('[', $thisPosition +1, ']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<xsl:when test="self::comment()"> <!-- Comment -->
<xsl:element name="nodeName">
<xsl:value-of select="'comment()'"/>
<xsl:variable name="thisPosition"
select="count(preceding-sibling::comment())"/>
<xsl:variable name="numFollowing"
select="count(following-sibling::comment())"/>
<xsl:if test="$thisPosition + $numFollowing > 0">
<xsl:value-of select=
"concat('[', $thisPosition +1, ']')"/>
</xsl:if>
</xsl:element>
</xsl:when>
<!-- Namespace: -->
<xsl:when test=
"count(. | ../namespace::*)
=
count(../namespace::*)">
<xsl:variable name="apos">'</xsl:variable>
<xsl:element name="nodeName">
<xsl:value-of select="concat('namespace::*',
'[local-name() = ', $apos, local-name(), $apos, ']')"/>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
<!-- <xsl:text>
</xsl:text> -->
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="$theResult"/>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document:
<div id="entry-1" class="item-asset asset hentry">
<div class="asset-header">
<h2 class="asset-name entry-title">
<a rel="bookmark" href="http://blahblah.com/paper-scissors">Paper Scissors</a>
</h2>
</div>
<div class="asset-content entry-content">
<div class="asset-body">
<p>Paper and scissors</p>
</div>
</div>
</div>
the result is a list of the XPath expressions for every node in the document:
<path>/div</path>
<path>/div/#id</path>
<path>/div/#class</path>
<path>/div/div[1]</path>
<path>/div/div[1]/#class</path>
<path>/div/div[1]/h2</path>
<path>/div/div[1]/h2/#class</path>
<path>/div/div[1]/h2/a</path>
<path>/div/div[1]/h2/a/#rel</path>
<path>/div/div[1]/h2/a/#href</path>
<path>/div/div[1]/h2/a/text()</path>
<path>/div/div[2]</path>
<path>/div/div[2]/#class</path>
<path>/div/div[2]/div</path>
<path>/div/div[2]/div/#class</path>
<path>/div/div[2]/div/p</path>
<path>/div/div[2]/div/p/text()</path>

is there a way to emulate correctly replace function on XPATH 1?

I have this function which tries to replace dots and/or - with _
I'm limited to use xpath 1 so replace function is NOT an option. The template works not to much fine because if I use something like this:
FOO-BAR.THING-MADRID.html
it gives me out on screen this thing:
FOO-BAR.THING-MADRID.html
the middle dot is not replaced.
Someone could help me?
<xsl:template name="replaceDots">
<xsl:param name="outputString"/>
<xsl:variable name="target">.</xsl:variable>
<xsl:variable name="source">-</xsl:variable>
<xsl:variable name="replacement">_</xsl:variable>
<xsl:choose>
<xsl:when test="contains($outputString,$source)">
<xsl:value-of select="concat(substring-before($outputString,$source),$replacement)" disable-output-escaping="yes"/>
<xsl:call-template name="replaceDots">
<xsl:with-param name="outputString" select="substring-after($outputString,$source)"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($outputString,$target)">
<xsl:value-of select="concat(substring-before($outputString,$target),$replacement)" disable-output-escaping="yes"/>
<xsl:call-template name="replaceDots">
<xsl:with-param name="outputString" select="substring-after($outputString,$target)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$outputString" disable-output-escaping="yes"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
To replace all dots or dashes with underscores, you do not need an <xsl:template>. You can use:
<xsl:value-of select="translate(., '-.', '__')" />
If you want to keep the ".html", you can extend this like so:
<xsl:value-of select="
concat(
translate(substring-before(., '.html'), '-.', '__'),
'.hmtl'
)
" />
For a generic "string replace" template in XSLT, look at this question, for example.

Removing the last characters in an XSLT string

In my CMS it is possible to create a new article, and choose an image to be shown on that article. When an image is chosen, a thumbnail of the image will automatically be created as well.
If the uploaded image is called image.jpg, then the corresponding thumbnail will automatically be named image_thumbnail.jpg.
I would now like to use the thumbnail image, everywhere on the website where the article is mentioned, except in the article itself (where the original big image should be shown).
But how can I do that?
I imagine if I could get the original name of the image, and then split it up before the suffix (.jpg, .png, .jpeg etc.) and hardcode _thumbnail after the name, then that would be sufficient.
In other words, I want to take the complete filename, and cut it into two parts, so that I can insert the string _thumbnail between the two parts.
Maybe that would work, but what if an image called image.2horses.jpg (a file with more than one dot in the filename) is uploaded? A naive cut before the '.' wouldn't work here.
Is there a way to get around this? Perhaps by cutting the filename up before the last 4 (.jpg, .png) or 5 (.jpeg) characters?
Off the top of my head:
<xsl:template name="substring-before-last">
<xsl:param name="string1" select="''" />
<xsl:param name="string2" select="''" />
<xsl:if test="$string1 != '' and $string2 != ''">
<xsl:variable name="head" select="substring-before($string1, $string2)" />
<xsl:variable name="tail" select="substring-after($string1, $string2)" />
<xsl:value-of select="$head" />
<xsl:if test="contains($tail, $string2)">
<xsl:value-of select="$string2" />
<xsl:call-template name="substring-before-last">
<xsl:with-param name="string1" select="$tail" />
<xsl:with-param name="string2" select="$string2" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
Called as:
<xsl:template match="/">
<xsl:variable name="filename" select="'image.2horses.jpg'" />
<xsl:variable name="basename">
<xsl:call-template name="substring-before-last">
<xsl:with-param name="string1" select="$filename" />
<xsl:with-param name="string2" select="'.'" />
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$basename" />
</xsl:template>
Yields:
image.2horses
Given the image's filename in $filename,
If you can assume that all images will end in ".jpg" and won't have ".jpg" elsewhere in the filename, then this should work:
<img src="{substring-before($filename, '.jpg')}_thumbnail.jpg" ... />
If you don't know the image type (like, you want to handle gif and png as well), or if you think the extension may occur multiple times in the filename ("image.jpg.jpg"), then you will want a template to help you:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<p>
<xsl:call-template name="image_thumbnail">
<xsl:with-param name="filename" select="'image.jpg'"/>
</xsl:call-template>
</p>
<p>
<xsl:call-template name="image_thumbnail">
<xsl:with-param name="filename" select="'image.09.07.11.jpg'"/>
</xsl:call-template>
</p>
<p>
<xsl:call-template name="image_thumbnail">
<xsl:with-param name="filename" select="'image.gif'"/>
</xsl:call-template>
</p>
<p>
<xsl:call-template name="image_thumbnail">
<xsl:with-param name="filename" select="'image with spaces.jpg'"/>
</xsl:call-template>
</p>
<p>
<xsl:call-template name="image_thumbnail">
<xsl:with-param name="filename" select="'image with irregular spaces.jpg'"/>
</xsl:call-template>
</p>
<p>
<xsl:call-template name="image_thumbnail">
<xsl:with-param name="filename" select="'image.jpg.again.jpg'"/>
</xsl:call-template>
</p>
</xsl:template>
<xsl:template name="image_thumbnail">
<xsl:param name="filename"/>
<xsl:choose>
<xsl:when test="contains($filename, '.')">
<xsl:variable name="before" select="substring-before($filename, '.')"/>
<xsl:variable name="after" select="substring-after($filename, '.')"/>
<xsl:choose>
<xsl:when test="contains($after, '.')">
<xsl:variable name="recursive">
<xsl:call-template name="image_thumbnail">
<xsl:with-param name="filename" select="$after"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="concat($before, '.', $recursive)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat($before, '_thumbnail.', $after)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$filename"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
A general solution involving only standard XSLT is somewhat hard since you have to search the string from the end. You can split your filename usings two functions, substring-before-last and substring-after-last. Unfortunately, these functions are not part of XSLT. You can Google and try to find implementations. Assuming you have these two functions implemented as XSLT templates you can then use the following template to generate thumbnail names:
<xsl:template name="thumbnail-name">
<xsl:param name="file-name"/>
<xsl:call-template name="substring-before-last">
<xsl:with-param name="text" select="$file-name"/>
<xsl:with-param name="chars" select="'.'"/>
</xsl:call-template>
<xsl:text>_thumbnail.</xsl:text>
<xsl:call-template name="substring-after-last">
<xsl:with-param name="text" select="$file-name"/>
<xsl:with-param name="chars" select="'.'"/>
</xsl:call-template>
</xsl:template>
You can use the template like this (assuming the variable $file-name contains the name of the image):
<img>
<xsl:attribute name="src">
<xsl:call-template name="thumbnail-name">
<xsl:with-param name="file-name" select="$file-name"/>
</xsl:call-template>
</xsl:attribute>
</img>
Have a look at the XPath functions overview at W3Schools, specifically the substring-before method.
I believe XPath functions operating on string might help you. I would try with some simple replace or translate.
XSLT 2 solution using regexp:
replace($filename, '(\.[^\.]*)$', concat('_thumbnail', '$1'))
Original answer (also XSLT 2):
This removes all after the last separator (including the separator). So below the $separatorRegexp could be '\.jpg' or just '\.' and the $separator '.jpg' or '.' in the other case.
string-join(reverse(remove(reverse(tokenize($filename, $separatorRegexp)),1)),$separator)
Eventually the '_thumbnail.jpg' can be appended with concat.

Resources