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>
Related
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.
I need to convert the date from 12 hour format to 24 hour format.
Input:
01/27/2016 07:01:36 PM
Expected output:
201601271901(YYYYMMDDHHMM)
I have used format-dateTime() function in my code ,I am getting error
<xsl:value-of select="format-dateTime(part_need/promised_dt,'[Y0001][M01][D01][H01][m01]')"/>
Error:
Description: FORG0001: Invalid dateTime value "01/27/2016 07:01:36 PM" (Non-numeric year component)
Please help on this issue
Your input is not a valid ISO 8601 date/time, so you cannot use the built-in date/time functions on it.
Try instead something like (XSLT 2.0):
<xsl:template match="inputdate">
<xsl:copy>
<xsl:variable name="dte" select="tokenize(.,'/|\s|:')" />
<xsl:value-of select="$dte[3]" />
<xsl:value-of select="$dte[1]" />
<xsl:value-of select="$dte[2]" />
<xsl:variable name="h24" select="xs:integer($dte[4]) mod 12 + 12 * xs:integer($dte[7]='PM')" />
<xsl:value-of select="format-number($h24, '00')" />
<xsl:value-of select="$dte[5]" />
</xsl:copy>
</xsl:template>
Note that this assumes your days are zero-padded to two digits (as are your months).
If you need to use this in several places, consider turning it into a function.
format-dateTime takes an xs:dateTime? as the first parameter. The part_needed/promised_dt is a node.
If you have the date-time in the standard ISO format (e.g. "2006-01-27T19:01:36"), you can use xs:dateTime(part_needed/promised_dt).
Saxon does not have a non-standard date-time parser helper, so you would need to use the xs:dateTime(xs:date(year,month,day), xs:time(hours, minutes, seconds)) constructor and use something like substring(part_needed/promised_dt,1,2) to get each date/time part.
I have a param named $SearchRecipe which holds a string that i'm always passing a lowercase string to.
I have an XML file which i'm accessing using my xslt and example of some data from the xml file :
<ARecipe>
<RecipeNo>117</RecipeNo>
<ItemName>Veggie Sausages with Beans and Chips</ItemName>
<RecipeInfo>
<Ingredients>Linda Mcartney Sausages(2 per serving), Beans (400g per serving), Chips(Handful), Random Chillis (up to you how much)</Ingredients>
<Instructions>Put on fat fryer, insert chips and sausages. Put on wok, insert beans and add some random chillis etc. Heat beans and remove cooked Sausages and Chips. Place sausages and chips in tissue to remove excess oil. Place in a plate and serve warm.</Instructions>
<RecipeType>Main</RecipeType>
<Vegetarian>No</Vegetarian>
</RecipeInfo>
</ARecipe>
I'm running a query to go through all the ItemName nodes and compare it to my string which works fine. For example if I put V in the string it matches the ItemName in this (ARecipe) and this gets displayed in my xslt output. However if I pass v as a value then it won't bring this particular node (ARecipe).
I'm using these lines to go through the xml file :
<xsl:variable name="matchedRecipes"
select="ARecipe[
($SearchType = 'Start' and starts-with($Test, $SearchRecipe)) or
($SearchType = 'Contains' and contains(ItemName, $SearchRecipe)) or
($SearchType = 'RecipeType' and
contains(RecipeInfo/RecipeType, $SearchRecipe))
]" />
<xsl:if test="$SearchType = 'Start' or $SearchType = 'Contains'">
<xsl:apply-templates select="$matchedRecipes">
What I have tried so far is this :
<xsl:variable name="Test">
<xsl:value-of select="translate('ARecipe/ItemName',$ucletters,$lcletters)"/>
</xsl:variable>
I'm a novice in most languages etc, but I would be able to do this kind of thing in C# fairly easily, i'm having an absolute nightmate with xslt. Also when I wrote this request for help, I had already been doing this work for a few hours and the last hour I've spent trying all sorts to get this lowercase issue to resolve. So if I've not asked properly, I would like to appologize in advance.
If you would like to see the larger picture I have uploaded my code here :
http://pastebin.com/w8AsiQRg
You'll need to do something like this:
<xsl:variable name="SearchLc" select="translate($SearchRecipe, $ucletters, $lcletters)" />
<xsl:variable name="matchedRecipes"
select="ARecipe[
($SearchType = 'Start' and
starts-with(translate(ItemName, $ucletters, $lcletters), $SearchLc)) or
($SearchType = 'Contains' and
contains(translate(ItemName, $ucletters, $lcletters), $SearchLc)) or
($SearchType = 'RecipeType' and
contains(translate(RecipeInfo/RecipeType, $ucletters, $lcletters),
$SearchRecipe))
]" />
You can get the lowercase version of the search text ahead of time, but then you'll need to do the lower-casing of the individual items inside the selection XPath.
I'm trying to map two documents witht the BizTalk Mapper and my target document should look like this:
<root>
<complexType>
<property>example</property>
</complexType>
<filler>
<padding>9999999</padding>
</filler>
<filler>
<padding>9999999</padding>
</filler>
<filler>
<padding>9999999</padding>
</filler>
</root>
The number of <filler> nodes that I should create is variable (from 0 to 9). It is basically the result of a calculation (based on some data provided in the source document).
Is there a way to create those <filler> nodes with some combination of functoids?
I tried to use the Table Looping functoid (created a table with only one column, the padding char '9') but doesn't really work because it creates as many <filler> nodes as rows are defined in the table, which is not what I want since the number of rows would have to be variable (again, based on a calculation).
What I currently do is pass the message (XmlDocument) to a C# method and then I programmatically append the <filler> nodes.
I'm hoping that there is a more "BizTalk-y" way of doing this with the Mapper.
I suspect that you will have to solve this problem by altering the XSLT.
Add some logic to create as many filler nodes as the result of your calculation dictates - you could create a template which you call in a loop perhaps, which would append a new filler section.
Hope this points you in the right direction.
As pointed out, XSLT can create nodes on the target document at will (I didn't know this and this was the key part).
Turns out that what I needed is a simple for-loop in XSLT. Once I realized this, a quick Google search yielded the following results:
http://quomon.com/question-How-to-make-a-for-loop-in-xslt-not-for-each-809.aspx
http://snippets.dzone.com/posts/show/930
Another thing worth noting is that (as pointed out by the first link), XSLT is a functional language, not procedural, so sometimes you do have to resort to using recursion or an extension.
This case is definitely one of those times since I couldn't use a careful selection of nodes using the select attribute on an xsl:for-each (since this filler data wasn't part of the source document).
Specifically, for this case, what I did was:
Add a Scripting functoid.
Add two inputs:
A constant with value "1" (this is the initial value of the i variable)
The length of the loop (number of times to repeat the body of the loop)
Paste the following XSLT template as an "Inline XSLT Call Template" script:
<xsl:template name="ForLoop">
<xsl:param name="i" /> <!-- index counter, 1-based, will be incremented with every recursive call -->
<xsl:param name="length" /> <!-- exit loop when i >= length -->
<!-- Output the desired node(s) if we're still looping -->
<!-- The base case is when i > length (in that case, do nothing) -->
<xsl:if test="$i <= $length">
<Filler>
<Padding>999999</Padding>
</Filler>
</xsl:if>
<!-- Call the ForLoop template recursively, incrementing i -->
<xsl:if test="$i <= $length">
<xsl:call-template name="ForLoop">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1"/>
</xsl:with-param>
<xsl:with-param name="length">
<xsl:value-of select="$length"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
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.