How to escape the # character in XSLT - 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

Related

Select first first child element in 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>

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>

XSLT Ignore namespace

I'm trying to get accustomed to XSLT, and I understand the reason for namespaces, but I'm simply trying to convert local XML files to be consumed by a local application.
I'm trying to convert the file found here: http://uscodebeta.house.gov/download/releasepoints/us/pl/113/31/xml_usc01#113-31.zip
using this code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" name="xml"/>
<xsl:template match="//title">
<xsl:for-each select="section">
<xsl:variable name="href"><xsl:value-of select="ancestor::title/num/#value" />-<xsl:value-of select="ancestor::chapter/num/#value" />-<xsl:value-of select="num/#value" />.xml</xsl:variable>
<xsl:result-document href="$href">
<xsl:element name="structure">
<xsl:element name="unit">
<xsl:attribute name="label">title</xsl:attribute>
<xsl:attribute name="identifier">
<xsl:value-of select="ancestor::title/num/#value" />
</xsl:attribute>
<xsl:attribute name="order_by">
<xsl:value-of select="ancestor::title/num/#value" />
</xsl:attribute>
<xsl:attribute name="level">1</xsl:attribute>
<xsl:value-of select="ancestor::title/num" /> <xsl:value-of select="ancestor::title/heading"/>
</xsl:element>
</xsl:element>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
into the XML example found here:
https://github.com/statedecoded/statedecoded/wiki/XML-Format-for-Parser
This is the conversion for just the first element, but when running with Saxon on the command line, I'm getting the warning:
Warning: SXXP0005: The source document is in namespace http://xml.house.gov/schemas/uslm/1.0, but all the template rules match elements in no namespace
and the output is plain text instead of XML tags.
Any help would be greatly appreciated.
Thanks
Since you're using XSLT 2.0 you can add the xpath-default-namespace attribute on xsl:stylesheet. See http://www.w3.org/TR/xslt20/#standard-attributes for more details.
For example:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://xml.house.gov/schemas/uslm/1.0">
You also have the option of using * as the prefix for each element in your paths. That could end up being a lot of work though if your stylesheet grows.
Example:
ancestor::*:title/*:num
Full example:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://xml.house.gov/schemas/uslm/1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:apply-templates select="#*|node()"/>
</xsl:template>
<xsl:template match="section">
<xsl:result-document href="{ancestor::title/num/#value}-{ancestor::chapter/num/#value}-{num/#value}.xml">
<structure>
<unit label="title" identifier="{ancestor::title/num/#value}"
order_by="{ancestor::title/num/#value}" level="1">
<xsl:value-of select="concat(ancestor::title/num,' ',ancestor::title/heading)"/>
</unit>
</structure>
</xsl:result-document>
</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

XSL if test starts-with

Given this piece of XML
<dc:date>info:eu-repo/date/embargoEnd/2013-06-12</dc:date>
<dc:date>2012-07-04</dc:date>
I should need with XSL to output only the year of the string not starting with info:eu-repo.
I'm trying this way, but it doesn't work. I'm wrong with the for-each?
<xsl:if test="not(starts-with('dc:date', 'info:eu-repo'))">
<xsl:for-each select="dc:date">
<publicationYear>
<xsl:variable name="date" select="."/>
<xsl:value-of select="substring($date, 0, 5)"/>
</publicationYear>
</xsl:for-each>
</xsl:if>
I guess you don't need ' in your start-with query, and you may want to iterate over dates slightly differently:
<xsl:for-each select="dc:date">
<xsl:variable name="date" select="."/>
<xsl:if test="not(starts-with($date, 'info:eu-repo'))">
<publicationYear>
<xsl:value-of select="substring($date, 0, 5)"/>
</publicationYear>
</xsl:if>
</xsl:for-each>
Use (assuming the provided XML fragment is elements that are children of the current node and there is only one element with the desired property):
substring-before(*[not(starts-with(., 'info:eu-repo'))], '-')
XSLT - based verification:
<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="/*">
<xsl:copy-of select=
"substring-before(*[not(starts-with(., 'info:eu-repo'))], '-') "/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the following XML document (the provided fragment wrapped in a single top element and the namespace declared):
<t xmlns:dc="some:dc">
<dc:date>info:eu-repo/date/embargoEnd/2013-06-12</dc:date>
<dc:date>2012-07-04</dc:date>
</t>
the XPath expression is evaluated off the top element and the result of this evaluation is copied to the output:
2012
II. More than one element with the desired property:
In this case It isn't possible to produce the desired data with a single XPath 1.0 expression.
This XSLT transformation:
<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="*[not(starts-with(., 'info:eu-repo'))]/text()">
<xsl:copy-of select="substring-before(., '-') "/>
==============
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on this XML document:
<t xmlns:dc="some:dc">
<dc:date>info:eu-repo/date/embargoEnd/2013-06-12</dc:date>
<dc:date>2012-07-04</dc:date>
<dc:date>info:eu-repo/date/embargoEnd/2013-06-12</dc:date>
<dc:date>2011-07-05</dc:date>
</t>
produces the wanted, correct result:
2012
==============
2011
==============
III. XPath 2.0 one-liner
*[not(starts-with(., 'info:eu-repo'))]/substring-before(., '-')
When this XPath 2.0 expression is evaluated off the top element of the last XML document (nearest above), the wanted years are produced:
2012 2011
XSLT 2.0 - based verification:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:sequence select=
"*[not(starts-with(., 'info:eu-repo'))]/substring-before(., '-')"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the last XML document, the XPath expression is evaluated and the result of this evaluation is copied to the output:
2012 2011
IV. The Most General and Difficult case:
Now, let's have this XML document:
<t xmlns:dc="some:dc">
<dc:date>info:eu-repo/date/embargoEnd/2013-06-12</dc:date>
<dc:date>2012-07-04</dc:date>
<dc:date>info:eu-repo/date/embargoEnd/2013-06-12</dc:date>
<dc:date>2011-07-05</dc:date>
<dc:date>*/date/embargoEnd/2014-06-12</dc:date>
</t>
We still want to get the year part of all dc:date elements whose string value doesn't start with 'info:eu-repo'. However none of the previous solutions work correctly with the last dc:date element above.
Remarkably, the wanted data can still be produced by a single XPAth 2.0 expression:
for $s in
*[not(starts-with(., 'info:eu-repo'))]/tokenize(.,'/')[last()]
return
substring-before($s, '-')
When this expression is evaluated off the top element of the above XML document, the wanted, correct result is produced:
2012 2011 2014
And this is the XSLT 2.0 - based verification:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:sequence select=
"for $s in
*[not(starts-with(., 'info:eu-repo'))]/tokenize(.,'/')[last()]
return
substring-before($s, '-')
"/>
</xsl:template>
</xsl:stylesheet>
You could also use a template-match to get what you want, for example:
<xsl:template match="date[not(starts-with(.,'info:eu-repo'))]">
<xsl:value-of select="."/>
</xsl:template>
I have this input XML:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<date>info:eu-repo/date/embargoEnd/2013-06-12</date>
<date>2012-07-04</date>
</list>
and apply this XSLT to it:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="text() | #*"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="date[not(starts-with(.,'info:eu-repo'))]">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
and I get this output:
<?xml version="1.0" encoding="UTF-8"?>2012-07-04