Unexpected behaviour of the XPath-axis preceding - xslt

I tried to make a list of all different values of the attribute year from the following XML
<Parts>
<Part Name="S1">
<Year year="2018" i="1"/>
<Year year="2017" i="2"/>
<Year year="2018" i="3"/>
<Year year="2017" i="4"/>
</Part>
</Parts>
with XSLT (with ant 1.8.2), using the axis preceding
<xsl:template match="/">
<xsl:for-each select="//Year [not( #year = preceding::Year/#year)]">
<xsl:sort select="#year" order="ascending" />
year: <xsl:value-of select="#year"/> -- <xsl:value-of select="./#i"/>
<br/><hr/>
</xsl:for-each>
</xsl:template>
getting the result
year: 2017 -- 2
year: 2018 -- 1
year: 2018 -- 3
I don't know, why I've got two times the "2018" (it is always the first of the selected elements giving the double).
With the axis preceding-sibling I get the expected result, a list with every value once:
year: 2017 -- 2
year: 2018 -- 1
Has anybody an explanation for this behaviour? I supposed preceding and preceding-sibling giving the same result in this example.
MK
Edit: Thanks for the comments.
I use the Java SE JDK 1.7.0
In the next time I will try to replace the processor by another implementation.

Last I knew, the version of Xalan packaged with the Sun JDKs was ancient and buggy. As Michael suggests, I would STRONGLY recommend obtaining the Apache versions until and unless Sun/Oracle fixes that.
(The XSLT processor packaged with IBM's JDKs is much less buggy -- it was being maintained in parallel with the Apache code until relatively recently, when that was replaced by a new processor written inside IBM. Alas, the IBM JDKs are not available separately; they ship only bundled with an IBM product that uses them.)

Related

Date compare in XSLT

I want to compare two dates in XSLT (1.0). Here I have mentioned hard coded dates
<xsl:variable name="DATE1" select="ms:format-date(16-FEB-19, 'dd-MMM-yy')" />
<xsl:variable name="DATE2" select="ms:format-date(01-MAY-19, 'dd-MMM-yy')" />
<xsl:if test="$DATE1 $lt; $DATE2">
</xsl:if>
I tried above but not getting proper result.
It looks like you're using some kind of extension function ms:format-date to format the dates. If you can format them as pure numeric YYYYMMDD then you can compare them as numbers. XSLT 1.0 does not offer a "<" operator for strings, let alone for dates.
Do think about moving forward to a later XSLT version (available from third parties) rather than asking the StackOverflow community to help you use a 20-year old version of the language.

Testing for 2 conditions under group-starting-with in <xsl:for-each-group>

I need to test for 1 of 2 possible tags in a for-each-group. Is this possible? What is the correct syntax?
I tried "||" but got an error from the parser. I'm using Saxon PE 9.3 in Oxygen 12.1.
Ex.
<xsl:param name="elements" as="element()*"/>
<xsl:for-each-group select="$elements"
group-starting-with="condition or condition" >
<xsl:for-each-group>
If you're wanting to group starting with either of two element names, try
<xsl:for-each-group select="$elements"
group-starting-with="foo | bar">

Counting XML based on date conditionals with xslt

I'm working with xslt for the first time. I have 2.0, but that's about the only advantage I have access to with the c# transform library we have. I'm trying to count a number of child nodes in the XML document which contain a date more than 12 years ago and have a certain type attribute.
Sample xml structure:
<xml version=\"1.0\" encoding=\"utf-8\"?>
<... />
<Dependents>
<Dependent><DOB>1964-04-01</DOB><DependentType>Spouse</DependentType></Dependent>
<Dependent><DOB>2000-01-01</DOB><DependentType>Child</DependentType></Dependent>
<Dependent><DOB>2012-01-01</DOB><DependentType>Child</DependentType></Dependent>
</Dependents>
<... />
where <... /> signifies some extra unrelated stuff.
So essentially, I want the number of children younger than 12 years old. (I have the count of dependenttype = child of all ages working, it's just the under 12 that's giving me trouble). The approach that was suggested to me was to construct a variable that stands for 12 years ago from today and use that as a basis for comparison in the count() function. That sounds reasonable, but I'm stuck constructing the date without the use of 3rd party libraries (e.g. exslt) that are so often linked in questions like this for the easy, handy-dandy answers.
The xslt I've gotten so far for this is:
<xsl:variable name="today" select="current-dateTime()" as="xs:dateTime" />
<xsl:variable name="twelveyearsago" select="xs:dateTime(concat(year-from-dateTime($today) - 12, '-', month-from-dateTime($today), '-', day-from-dateTime($today)))" />
<xsl:text>12yearsago=</xsl:text><xsl:value-of select="$twelveyearsago" />
And it's not working because the month-from-dateTime (and presumable day-from-dateTime) don't add leading zeros. For today, March 21 2012 I'm getting back: Saxon.Api.DynamicError : Invalid dateTime value "2000-3-21" (Month must be two digits) (The W3Schools xpath function reference implies that they should, but they don't.)
What I would like to output is:
<xsl:text>&numberofchildren=</xsl:text><xsl:value-of select="count(//InsuranceRequest/HealthInsurance/Dependents/Dependent/DependentType[text() = 'Child'])" />
<xsl:text>&childrenunder12=</xsl:text><xsl:value-of select="children under twelve" />
The more I pound my head against this, the more I feel like there's a simpler approach that I'm just not seeing.
Edit: I cleaned up the xslt syntax so it's valid and not a doubly-quoted c# string.
You can simply substract a duration of 12 years as in <xsl:variable name="twelveyearsago" select="$today - xs:yearMonthDuration('P12Y')"/>, then use e.g. //Dependent[DependentType = 'Child' and xs:date(DOB) >= $twelveyearsago].
[edit]
Here is a template that compiles and executes fine for with Saxon 9.4:
<xsl:template match="/">
<xsl:variable name="today" select="current-date()"/>
<xsl:variable name="twelve-years-ago" select="$today - xs:yearMonthDuration('P12Y')"/>
<xsl:value-of select="count(//Dependent[DependentType = 'Child' and xs:date(DOB) >= $twelve-years-ago])"/>
</xsl:template>

Paragraph needs to have 2lines or 3lines of sentences not just one line [duplicate]

This question already exists:
Closed 11 years ago.
Possible Duplicate:
In my xml feed i have … … replace it with period. and space & also i need to <p> tag after every two or three senctences using xsl
This is XML:
<data>The production of opium itself has basically not changed since
ancient times...Opium trade became more regular by the seventeenth
century, when it was mixed with tobacco for smoking, and addiction was
first recognized... This is a test Message3…This is showing off a
handful of updates to its line of audio accessories this week at IFA
in Berlin. At top of the list is the newly revealed inAir 5000, a
hefty tabletop AirPlay speaker that the company is firmly positioning
to take on Bowers&Wilkins' Zeppelin line (which also recently got its
own AirPlay version)... Like that system, the inAir certainly offers a
unique take on aesthetics, with a teardrop design. The company opted
not to install an Apple dock on the 110 watt system, given that
compatible devices can stream audio wirelessly to the thing via
AirPlay...Twice a month, tourists board a bus and embark on a "fact-finding mission" to one of the hottest spots in the immigration debate -- the Arizona-Mexico border. Tourists are encouraged to make make up their own minds..."Contagion" is an action-thriller about the pandemic outbreak of a deadly virus. It is part fantasy, part reality and totally possible, says one global health expert...The families are trying to raise the bail money.</data>
using xsl
Paragraph needs to split into three equal lines,but not just one like and one another thing is i need to remove '...', '…' and replace it with just period.
<p>This is showing off a handful of updates to its line of audio accessories this week at IFA in Berlin. At top of the list is the newly revealed inAir 5000, a hefty tabletop AirPlay speaker that the company is firmly positioning to take on Bowers&Wilkins' Zeppelin line (which also recently got its own AirPlay version).</p>
<p>Like that system, the inAir certainly offers a unique take on aesthetics, with a teardrop design. The company opted not to install an Apple dock on the 110 watt system, given that compatible devices can stream audio wirelessly to the thing via AirPlay.Twice a month, tourists board a bus and embark on a "fact-finding mission" to one of the hottest spots in the immigration debate -- the Arizona-Mexico border. Tourists are encouraged to make make up their own minds.</p>
"Contagion" is an action-thriller about the pandemic outbreak of a deadly virus. It is part fantasy, part reality and totally possible, says one global health expert.The families are trying to raise the bail money.
I hope this answer is what you need:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xsl:variable name="count" select="string-length(.)"/>
<xsl:variable name="firstparagraph" select="substring(., 1, string-length(.)-($count*0.67))"/>
<xsl:variable name="afterfirstparagraph" select="substring-after(., $firstparagraph)"/>
<xsl:variable name="countafterfirstparagraph" select="string-length($afterfirstparagraph)"/>
<xsl:variable name="secondparagraph" select="substring($afterfirstparagraph, 1, string-length($afterfirstparagraph)-($countafterfirstparagraph*0.5))"/>
<xsl:element name="data">
<xsl:element name="p">
<xsl:value-of select="replace(replace(substring(., 1, string-length(.)-($count*0.67)), '\.\.\.', '.'), '…', '.')"/>
</xsl:element>
<xsl:element name="p">
<xsl:value-of select="replace(replace(substring($afterfirstparagraph, 1, string-length($afterfirstparagraph)-($countafterfirstparagraph*0.5)), '\.\.\.', '.'), '…', '.')"/>
</xsl:element>
<xsl:element name="p">
<xsl:value-of select="replace(replace(substring-after(., $secondparagraph), '\.\.\.', '.'), '…', '.')"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Please note that you have to replace "&" with its unicode.

date minus another date in xslt

Hope someone can help. I am trying to compare 2 dates inside an XML file and using a XSLT to do some calculation:
For example I have 2 dates in XML: 2011-05-23 and 2011-04-29. I want to do calculation inside XSLT like below:
('2011-05-23'-'2011-04-29')*30 = 24*30= 720
Can anyone shed any light?
An XSLT 2.0 solution
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="/">
<xsl:value-of select="days-from-duration(
xs:date('2011-05-23')
- xs:date(xs:date('2011-04-29'))
)*30"/>
</xsl:template>
</xsl:stylesheet>
Yields: 720
xs:date() function evaluates the dates, which can be used to perform date operations
subtracting the second date from the first yields the xdt:dayTimeDuration P24D (24 days)
days-from-duration() extracts the days component from the xdt:dayTimeDuration (24)
then you can use that number to perform normal arethmatic (e.g. 24*30=720)
It might be worth looking at the EXSLT - date:difference solution. I believe that should do what you want, and there's even a straight XSLT implementation available.
Be aware though that the returned value is in duration format as specified in the XML Schema Part 2: Datatypes Second Edition, so it's likely you will need to process the result to derive the unit you wish to use in calculation (for instance in your example above you're expecting a result detailing the number of days difference - so you would need to pull out the relevant unit you want to work with, the result from date:difference in that case would most likely be "P24D").
Here's two templates I sometimes use for date calculations:
<xsl:template name="calcseconds">
<xsl:param name="date" />
<xsl:value-of select="(((substring($date,1,4) - 1970) * 365)+floor((substring($date,1,4) - 1970) div 4)+substring('000,031,059,090,120,151,181,212,243,273,304,334,365',substring($date,6,2)*4-3,3)+(substring($date,9,2)-1)+(1-floor(((substring($date,1,4) mod 4) + 2) div 3))*floor((substring($date,6,2)+17) div 20))*86400+(substring($date,12,2)*3600)+(substring($date,15,2)*60)+substring($date,18,2)" />
</xsl:template>
<xsl:template name="calcdays">
<xsl:param name="date" />
<xsl:value-of select="(((substring($date,1,4) - 1970) * 365)+floor((substring($date,1,4) - 1970) div 4)+substring('000,031,059,090,120,151,181,212,243,273,304,334,365',substring($date,6,2)*4-3,3)+(substring($date,9,2)-1)+(1-floor(((substring($date,1,4) mod 4) + 2) div 3))*floor((substring($date,6,2)+17) div 20))" />
</xsl:template>
They're a bit of a mouthful, but they'll calculate the number of seconds/days since midnight 1st January 1970 as an integer, which you can then do straight arithmetic on. They rely on the date format being yyyy-mm-dd hh:mm:ss, but manipulation of the parameters of the substring calls should allow you to process dates in any format you need.