XSL media:content - Trimming strings in XSLT - xslt

Can anyone tell me how to select the URL from this:
<media:content url="http://feedproxy.google.com/~r/TEDTalks_video/~5/aZWq6PY05YE/TimBrown_2009G.mp4" fileSize="57985745" type="video/mp4" />
I want to:
Create a link to this file:
Trim the URL from:
http://feedproxy.google.com/~r/TEDTalks_video/~5/aZWq6PY05YE/TimBrown_2009G.mp4
to:
TimBrown_2009G
and then take: TimBrown_2009G and use it as part of a URL

Selecting the URL. You just need to make sure that you have the correct namespace URI.
<xsl:value-of xmlns:media="http://search.yahoo.com/mrss/"
select="media:content/#url"/>
Trimming down the URL. How to do this best depends on whether you are using XSLT 1 or 2, since the latter has better string handling functions from XPath 2.0.
If you are using XSLT 1, you might want to create a helper template to return the last segment out of a delimited string:
<xsl:template name="last-substring-after">
<xsl:param name="string"/>
<xsl:param name="separator"/>
<xsl:choose>
<xsl:when test="contains($string, $separator)">
<xsl:call-template name="last-substring-after">
<xsl:with-param name="string"
select="substring-after($string, $separator)"/>
<xsl:with-param name="separator"
select="$separator"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Then you can make use of that to extract the last segment of the URL, and proceed to extract the part before the dot. Assuming the URL is in variable url:
<xsl:variable name="name">
<xsl:call-template name="last-substring-after">
<xsl:with-param name="string" select="$url"/>
<xsl:with-param name="separator" select="'/'"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="substring-before($name, '.')"/>

Related

Sum value of output of template in Apache FOP

I am using Apache FOP to generate a PDF document, and to display a certain value I have to iterate over a number of nodes to determine a total price value, then sum that value. So far I have a function that iterates over an array and then retrieves the intended value, but the issue occurs when I try to sum the results.
<xsl:function name="foo:buildTotalValue">
<xsl:param name="items" />
<xsl:variable name="totals">
<xsl:for-each select="$items/charge">
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="sum(exsl:node-set($totals))" />
</xsl:function>
<xsl:template name="getTotalPriceNode">
<xsl:param name="itemParam" />
<xsl:choose>
<xsl:when test="$itemParam/Recurrance = 'OnceOff'">
<xsl:value-of select="$itemParam/TotalValue" />
</xsl:when>
<xsl:when test="$itemParam/Recurrance = 'Monthly'">
<xsl:value-of select="$itemParam/TotalValue * $itemParam/Months"/>
</xsl:when>
<xsl:otherwise><xsl:value-of select="0" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
I'm hoping that when I pass in foo:buildTotalValue with entries like this:
<Charges>
<Charge>
<Recurrance>OnceOff</Recurrance>
<TotalValue>50.00</TotalValue>
</Charge>
<Charge>
<Recurrance>Monthly</Recurrance>
<TotalValue>10.00</TotalValue>
<Months>6</Months>
</Charge>
</Charges>
would return with the value 110.00, but instead I get the error:
Cannot convert string "50.0060.00" to double
I've tried adding a <value> or something in the templates and then using that as a selector for the exsl:node-set function but it doesn't seem to make a difference.
AFAICT, the problem with your function is that it builds a concatenated string of values returned by the called template, instead of a tree of nodes that can be converted into a node-set and summed.
Try changing:
<xsl:for-each select="$items/charge">
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</xsl:for-each>
to:
<xsl:for-each select="$items/charge">
<total>
<xsl:call-template name="getTotalPriceNode">
<xsl:with-param name="itemParam" select="." />
</xsl:call-template>
</total>
</xsl:for-each>
and:
<xsl:value-of select="sum(exsl:node-set($totals))" />
to:
<xsl:value-of select="sum(exsl:node-set($totals)/total)" />
Untested, because (see comment to your question).
I ended up using the suggestion from Martin from the comment - the xpath 2+ expression along the line of:
sum(Charge[Recurrance = 'OnceOff']/TotalValue | Charge[Recurrance = 'Monthly']/(TotalValue * Months))
which was able to achieve what I needed without the use of functions / templates / node-set (And in a lot less code)

How to replace variable inside curly braces with values in different tags in xslt

I am processing an xml file using xslt.
<ns1:declarationStatements>
<ns1:parameterisedEntity>
<ns2:code>NUTSUPSTATE20</ns2:code>
<ns2:localeData>
<ns1:description>
<![CDATA[** When {s} according to instructions {m}g typically weighs {m}g.]]>
</ns1:description>
<ns1:id>20253</ns1:id>
</ns2:localeData>
<ns2:specType>FOOD</ns2:specType>
<ns2:id>6653</ns2:id>
</ns1:parameterisedEntity>
<ns1:textParameters>
<ns1:value>228</ns1:value>
<ns1:id>68225</ns1:id>
<ns1:sequence>2</ns1:sequence>
</ns1:textParameters>
<ns1:textParameters>
<ns1:value>cooked</ns1:value>
<ns1:id>68233</ns1:id>
<ns1:sequence>0</ns1:sequence>
</ns1:textParameters>
<ns1:textParameters>
<ns1:value>255</ns1:value>
<ns1:id>68229</ns1:id>
<ns1:sequence>1</ns1:sequence>
</ns1:textParameters>
<ns1:id>133421</ns1:id>
</ns1:declarationStatements>
I want to get the text inside <ns1:description> i.e.-
**When {s} according to instructions {m}g typically weighs {m}g
But I want {s}, {m} and {m} to be replaced by the values in <ns1:textParameters>/<ns1:value>. It should look like -
**When cooked according to instructions 255g typically weighs 228g.
I tried doing that by using <xsl:value-of select="ns0:declarationStatements"> and the manipulating string but it is becoming very tedious and complex.
The number of such braces may also vary. So do we have anything like List or Array in XSLT?
Is there any other way or trick I can use to solve this problem?
Thanks
Assuming the parameters are meant to be inserted in order of their ns1:sequence value, I would start by defining a key as:
<xsl:key name="text-param" match="ns1:textParameters" use="ns1:sequence" />
then call the following recursive template with ns1:description as the string param:
<xsl:template name="merge-params">
<xsl:param name="string"/>
<xsl:param name="i" select="0"/>
<xsl:choose>
<xsl:when test="contains($string, '{') and contains(substring-after($string, '{'), '}')">
<xsl:value-of select="substring-before($string, '{')" />
<xsl:value-of select="key('text-param', $i)/ns1:value" />
<!-- recursive call -->
<xsl:call-template name="merge-params">
<xsl:with-param name="string" select="substring-after($string, '}')" />
<xsl:with-param name="i" select="$i + 1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>

XSL 1.0 How to replace two different things in a string

I've got a string that could have line breaks and apostrophes. I need to do a replace for both. I have the following xsl code:
<xsl:call-template name="replaceapostrophes">
<xsl:with-param name="string">
<xsl:call-template name="replacelinefeeds">
<xsl:with-param name="string" select="hl7:text/hl7:paragraph"/>
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
<!-- template for replacing line feeds with <br> tags for page display -->
<xsl:template name="replacelinefeeds">
<xsl:param name="string"/>
<xsl:choose>
<xsl:when test="contains($string,'
')">
<xsl:value-of select="substring-before($string,'
')"/>
<br/>
<xsl:call-template name="replacelinefeeds">
<xsl:with-param name="string" select="substring-after($string,'
')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- template for replacing html encoded apostrophes for page display -->
<xsl:template name="replaceapostrophes">
<xsl:param name="string"/>
<xsl:choose>
<xsl:when test="contains($string, '&#39;')">
<xsl:value-of select="substring-before($string,'&#39;')"/>'<xsl:call-template name="replaceapostrophes">
<xsl:with-param name="string" select="substring-after($string,'&#39;')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This is the xml code:
<text>
<paragraph>Adding apostrophe to the patient&#39;s instructions
and checking for a second line</paragraph>
</text>
However, when this runs, I'm getting the apostrophe accounted for, but not the line breaks.
Adding apostrophe to the patient's instructions and checking for a second line
rather than
Adding apostrophe to the patient's instructions
and checking for a second line
It does work fine if there's one or the other but not both in the same string.
Is there a different way I need to do these?
Thanks
Try doing it the other way around (replace apostrophes first, then line breaks).
Basically you are putting an HTML <br/> element into your variable, and then taking its text value to replace the apostrophes , which removes the line break again.
Use the templates the other way round, i.e. first replace the apostrophes, then the line feeds. And make sure for the result of the replacement of line feeds you use xsl:copy-of and not xsl:value-of when you output it, as otherwise the br elements will be lost. So in case you have <xsl:variable name="text"><xsl:call-template name="replacelinefeeds">..</xsl:call-template></xsl:variable>, make sure you use <xsl:copy-of select="$text"/>.

Print the same value many times in xslt

I have the variable x which is a number. I have a line. ("<name>James</name>") I need to print this sentence number x times. Can I do it in an easy way? without being complex?
If you are using XSLT 2.0 then you can do this ...
<xsl:for-each select="for $i in 1 to $x return $i">
<name>James</name>
</xsl:for-each>
The following is untested...
<xsl:call-template name="show">
<xsl:with-param name="text"><name>James</name></xsl:with-param>
<xsl:with-param name="count">50</xsl:with-param>
</xsl:call-template>
<xsl:template name="show">
<xsl:param name="text"/>
<xsl:param name="count"/>
<xsl:value-of select="$text"/>
<xsl:if test="number($count)>0">
<xsl:call-template name="show">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="count" select="number($count)-1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Updated to have <name> and </name>.
Here is an XmlPlayground of the above working
You could adding the following somewhere in your stylesheet:
<mydata>
<x/><x/><x/><x/> <!-- to print four times -->
</mydata>
then
<xsl:for-each select="document()//mydata/x">
<name>James</name>
</xsl:for-each>
This takes advantage of the ability to include your own data in an XSLT program, and access it through the document function (no argument indicates the stylesheet itself).

XSL Rookie -Can I make an XSL that matches "this phrase" also match "ths fraze" without repeating the entire "if test" block?

I'm a complete XSL novice, writing an XSL file to format lots of different error messages that can appear in the output log created by an application into CSV format.
Slight variations might occur in the matchable tags in these output logs. For example, one sentence in the log might contain the phrase "Service Month/Year:" but another one, from a different area of the application, would contain "Svc Month/Yr:" instead.
Is there a way to put both those variations of that phrase in a single line of my XSL? Or do I have to repeat the entire If block, with each variation in the phrase that I want to match on in its own If block?
I tried posting the XSL here surrounded by backticks, but it runs all together in one big lump that's impossible to read. If anyone can help with this question, I'm happy to post it if you tell me how to make it readable. :-)
Thank you.
XSL allows combining of conditional statements like other langues. Each one doesn't require its on if statement. Were you thinking something along these lines?
<xsl:choose>
<xsl:when test="contains(text(), 'Service Month/Year:')
or contains(text(), 'Svc Month/Yr:')
">
<!-- do something -->
</xsl:when>
</xsl:choose>
Keep in mind xml/xsl is case sensitive. To make it more flexible, it is even more verbose:
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:choose>
<xsl:when test="contains(translate(text(), $upper, $lower), 'service month/year:')
or contains(translate(text(), $upper, $lower), 'svc month/yr:')
">
<!-- do something -->
</xsl:when>
</xsl:choose>
EDIT: And an even better answer I whipped up
<xsl:template name="containsToken">
<xsl:param name="inputString"/>
<xsl:param name="tokens"/>
<xsl:param name="delimiter"/>
<xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
<xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:choose>
<xsl:when test="contains($tokens, $delimiter)">
<xsl:variable name="token">
<xsl:value-of select="substring-before($tokens, $delimiter)"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains(translate($inputString, $upper, $lower), translate($token, $upper, $lower))">
<xsl:text>True</xsl:text>
</xsl:when>
<xsl:otherwise>
<!-- loop -->
<xsl:call-template name="containsToken">
<xsl:with-param name="inputString" select="$inputString"/>
<xsl:with-param name="tokens" select="substring-after($tokens, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="contains(translate($inputString, $upper, $lower), translate($tokens, $upper, $lower))">
<xsl:text>True</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>False</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Usage:
<xsl:variable name="found">
<xsl:call-template name="containsToken">
<xsl:with-param name="inputString" select="text()"/>
<xsl:with-param name="tokens" select="'Service Month/Year:|Svc Month/Yr:'"/>
<xsl:with-param name="delimiter" select="'|'"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$found = 'True'">
<!-- process -->
</xsl:if>
Delimiter can be whatever character or charcters you want. Tokens is a list of things to search for with the delimiter between each one. Enjoy!