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

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.

Related

XSLT - More than one validation

I am not a programmer and need some help from the pros. If I have more than one validation I want to check for exampel I want this result when test = 78 or 81 otherwise.
I have tried using without succcess
<xsl:variable name = "vatTerm">
<xsl:choose>
<xsl:when test="(DocumentXML/ApplicationObject/Object/CarTypeId!='78') and (CarTypeId!='81')">
<xsl:value-of select="(($netterm+$termfee)*$vatrate )+$eptermfeetotalvat"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select ="$eptermfeetotalvat"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
Also tried;
<xsl:choose>
<xsl:when test="(DocumentXML/ApplicationObject/Object/CarTypeId!=78">
<xsl:value-of select="(($netterm+$termfee)*$vatrate )+$eptermfeetotalvat"/>
</xsl:when>
<xsl:when test="(DocumentXML/ApplicationObject/Object/CarTypeId!=81">
<xsl:value-of select="(($netterm+$termfee)*$vatrate )+$eptermfeetotalvat"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select ="$eptermfeetotalvat"/></xsl:otherwise>
</xsl:choose>
You got the DeMorgan right.
But you forgot that the path's base is not duplicated. So, I guess, you should try
<xsl:when test="DocumentXML/ApplicationObject/Object/CarTypeId!='78' and DocumentXML/ApplicationObject/Object/CarTypeId!='81'">
<xsl:value-of select="(($netterm+$termfee)*$vatrate )+$eptermfeetotalvat"/>
</xsl:when>
This approach has the restriction that it checks if any CarTypeId at the given path is unequal to 78 or 81. If you have only one CarTypeId, this will be working fine with XPath-1.0.
With XPath-2.0 you could simplify and precise this task.
I would use the one predicate like this
<xsl:choose>
<xsl:when test="DocumentXML/ApplicationObject/Object/CarTypeId[.!='78' and .!='81']">
<xsl:value-of select="(($netterm+$termfee)*$vatrate )+$eptermfeetotalvat"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select ="$eptermfeetotalvat"/>
</xsl:otherwise>
</xsl:choose>

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>

Set Union Operator in xpath

<xsl:variable name="targetReceiverService">
<EMP_EMPLOC_MAL curr="4.0">MAL</EMP_EMPLOC_MAL>
<EMP_EMPLOC_SIN curr="1.6">SIN</EMP_EMPLOC_SIN>
<EMP_EMPLOC_CHN curr="7.8">CHN</EMP_EMPLOC_CHN>
<DEFAULT curr="1.0">NONE</DEFAULT>
</xsl:variable>
<xsl:variable name="targetCountryCode" select="$targetReceiverService/*[name() = $ReceiverService] | $targetReceiverService/DEFAULT"/>
<xsl:value-of select="$targetCountryCode "/>
why value display for $targetCountryCode is only MAL but not included NONE since the "|" mean
xsl:value-of only displays the value of the first node in the set. (at least with XSL 1)
You can probably display all of them with
<xsl:for-each select="$targetCountryCode">
<xsl:value-of select="."/>
</xsl:for-each>

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>

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