Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>
like image 407
robert laing Avatar asked Sep 20 '12 08:09

robert laing


People also ask

When statement in xsl?

The <xsl:when> element is used to specify an action for the <xsl:choose> element. The <xsl:when> element evaluates an expression, and if it returns true, an action is performed.

What are the two instructions in xsl that allow you to conditionally process an element based on specific test conditions?

Almost all programming languages have conditional branching structures. XSL has two: <xsl:if> and <xsl:choose>. XPath also has the if-then-else structure.

How do I substring in XSLT?

Substring is primarily used to return a section of a string or truncating a string with the help of the length provided. XSLT is incorporated with XPath while manipulating strings of text when XSLT access. The Substring function takes a string as an argument with the numbers one or two and an optional length.


3 Answers

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>
like image 190
DRCB Avatar answered Oct 28 '22 23:10

DRCB


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>
like image 22
Dimitre Novatchev Avatar answered Oct 28 '22 23:10

Dimitre Novatchev


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
like image 2
Peter Avatar answered Oct 28 '22 21:10

Peter