Select first first child element in XSLT - xslt

The <graphic> element to be used from the input XML should be the first child element of the <body> element. How Can I check this using XSLT
Input :
<body>
<graphic>Image</graphic>
<p>Text</p>
</body>
Output of above should be true. I am using XSLT 2.0

Check it
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="body">
<xsl:if test="*[1][local-name()='graphic']"><xsl:text>Yes</xsl:text></xsl:if>
</xsl:template>
</xsl:stylesheet>

You can try it
<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:template match="/">
<xsl:choose>
<xsl:when test="body/*[1][local-name()='graphic']">
<xsl:text>True</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>False</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

If you want a result of true or false, you can do simply:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="body">
<xsl:value-of select="name(*[1])='graphic'"/>
</xsl:template>
</xsl:stylesheet>

Check this:
<xsl:template match="/">
<xsl:value-of select="if(body/*[1][self::graphic]) then 'True' else 'False'"/>
</xsl:template>

Related

How to calculate positions of XML elements using XSL?

I have a list of items in XML (mind the duplicates):
<root>
<a>hello</a>
<a>bye</a>
<a>5</a>
<a>hello</a>
<a>8</a>
</root>
I want to translate it to this:
<root>
<a>4</a>
<a>3</a>
<a>1</a>
<a>4</a>
<a>2</a>
</root>
Essentially, I'm replacing values with their positions in a sorted list of all values (comparing text to text). I'm trying to do this using <xsl:key>, but can't figure out how exactly.
Try perhaps:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<xsl:variable name="sorted">
<xsl:perform-sort select="a">
<xsl:sort select="."/>
</xsl:perform-sort>
</xsl:variable>
<root>
<xsl:for-each select="a">
<a>
<xsl:value-of select="index-of(distinct-values($sorted/a), .)"/>
</a>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Or maybe a bit more elegantly:
<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:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<xsl:variable name="sorted" as="xs:string*">
<xsl:perform-sort select="distinct-values(a)">
<xsl:sort select="."/>
</xsl:perform-sort>
</xsl:variable>
<root>
<xsl:for-each select="a">
<a>
<xsl:value-of select="index-of($sorted, .)"/>
</a>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Or even just simply:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/root">
<xsl:variable name="sorted">
<xsl:perform-sort select="a">
<xsl:sort select="."/>
</xsl:perform-sort>
</xsl:variable>
<root>
<xsl:for-each select="a">
<a>
<xsl:value-of select="index-of($sorted/a, .)[1]"/>
</a>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>

XSL 1.0 If not like

I'm using XSL to create a PDF document template, and don't want certain fields to display if the line value is zero.
I have tried
<xsl:if test="line_value != 0">
<xsl:with-param name="value" select="unit_quantity"/>
</xsl:if>
But this doesn't work. I think because line_value is of the format £0.00.
I'm trying to get it to do line_value NOT LIKE '£0.00', but I don't think that's the correct syntax for XSL.
I am assuming that below is your XML:
INPUT:
<?xml version="1.0" encoding="utf-8" ?>
<body>
<line_value>£0.00</line_value>
<line_value>£1.00</line_value>
<line_value>£0.00</line_value>
<line_value>£2.00</line_value>
<line_value>£0.00</line_value>
<line_value>£5.00</line_value>
<line_value>£0.00</line_value>
<line_value>£1.00</line_value>
</body>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:for-each select="body/line_value">
<xsl:if test="number(translate(., '£', '')) != 0">
<num>
<xsl:value-of select="."/>
</num>
</xsl:if>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>

XSLT manipulate text scattered across various nodes

Input file as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!-- lower UPPER case -->
<document>
<rubbish> rubbish </rubbish>
<span class='lower'>
lower
<span class='upper'> upper </span>
case
</span>
</document>
Wanted output:
lower UPPER case
I know how to get the text included in the outer span with value-of, but this also
includes the string "upper" unchanged which is not what I want. I do not know how
to manipulate the text in the inner span and insert it in the middle of
the other text.
Failed attempt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text" indent="no"/>
<xsl:template match="/">
<xsl:for-each select="//span[#class = 'lower']">
<xsl:if test="span/#class = 'upper'">
<xsl:text>do something</xsl:text> <!--TO DO -->
</xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
You need to take a recursive approach here, for example:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="text()[parent::span]">
<xsl:choose>
<xsl:when test="../#class='upper'">
<xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
To understand how this works, read up on built-in template rules: http://www.w3.org/TR/xslt/#built-in-rule
The following approach does away with the <choose> and completely pushes the problem down to the match expression:
<?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" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="text()"/>
<xsl:template match="text()[parent::span[#class = 'upper']]">
<xsl:value-of select="translate(., 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')"/>
</xsl:template>
<xsl:template match="text()[parent::span[#class = 'lower']]">
<xsl:value-of select="translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
</xsl:template>
</xsl:stylesheet>

in xslt2.0 , how to add extra minute value to existing time stamp

I had a time stamp in my input xml as below
2012-01-19T21:36:33.085+01:00
now i want to add some minute value to it . the different values of minutes are as below
30,-30,60,-60
Is there any xslt function to do this addition and give the output as of type xs:dateTime
You can cast your minutes as a duration and then add/subtract. (Subtract if it's a negative number.)
Example...
XML Input
<test>2012-01-19T21:36:33.085+01:00</test>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" extension-element-prefixes="xs">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="minutesToAdd" select="30" as="xs:integer"/>
<xsl:template match="/*">
<results>
<xsl:choose>
<xsl:when test="0 > $minutesToAdd">
<xsl:value-of select="xs:dateTime(.) - xs:dayTimeDuration(concat('PT',abs($minutesToAdd),'M'))"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="xs:dateTime(.) + xs:dayTimeDuration(concat('PT',$minutesToAdd,'M'))"/>
</xsl:otherwise>
</xsl:choose>
</results>
</xsl:template>
</xsl:stylesheet>
Output
<results>2012-01-19T22:06:33.085+01:00</results>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
>
<xsl:template name="main">
<xsl:value-of select="xs:dateTime('2012-01-19T21:36:33.085+01:00') +
xs:dayTimeDuration('PT0H30M')"/>
</xsl:template>
</xsl:stylesheet>
produces
2012-01-19T22:06:33.085+01:00

How to escape the # character in XSLT

$binding-path contains something like Contact!ShowsInterest which should be converted to Contact/#ShowsInterest
This is what i tried so far:
<xsl:variable name="bindpath" select="translate($binding-path, '!','/#')" />
<xsl:value-of select="concat('{Binding XPath=',$bindpath,'}')"/>
or
<xsl:variable name="bindpath" select="translate($binding-path, '!','/#')" />
<xsl:value-of select="concat('{Binding XPath=',$bindpath,'}')"/>
But no matter what i try, the result is always Contact/ShowsInterest
The translate() function can only replace every occurence of a single characterwith a single character (or with nothing, thus deleting it).
Use:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="binding-path" select="'Contact!ShowsInterest'"/>
<xsl:template match="/">
<xsl:variable name="bindingpath">
<xsl:value-of select="substring-before($binding-path, '!')"/>
<xsl:text>/#</xsl:text>
<xsl:value-of select="substring-after($binding-path, '!')"/>
</xsl:variable>
<xsl:value-of select="$bindingpath"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted, correct result is produced:
Contact/#ShowsInterest
II. XSLT 2.0
Use the XPath 2.0 replace() function:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="binding-path" select="'Contact!ShowsInterest'"/>
<xsl:template match="/">
<xsl:variable name="bindingpath" select="replace($binding-path, '!', '/#')"/>
<xsl:value-of select="$bindingpath"/>
</xsl:template>
</xsl:stylesheet>
This transformation produces the same correct result:
Contact/#ShowsInterest