group function in xslt [closed] - xslt

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
I need help with a little problem in XSLT
I have a XML :
a.xsm
<?xml version="1.0" encoding="UTF-8"?>
<ExtData>
<table bName="B SERs" id="BSER">
<Col bName="Bus" id="BUS">
<CoreCol coreEName="SER" coreHref="../_a2.xml" corebName="Bus" coreId="BUS"/>
</Col>
<Col bName="Bus" id="BUS">
<CoreCol coreEName="MOR" coreHref="../_a5.xml" corebName="Busw" coreId="BUSW"/>
</Col>
<Col bName="Ser" id="NAME">
<CoreCol coreEName="SER" coreHref="../_a2.xml" corebName="Ser" coreId="NAME"/>
</Col>
<Col bName="ID" id="ID">
<CoreCol coreEName="SER" coreHref="../_a2.xml" corebName="SerId" coreId="UCMDB_ID"/>
</Col>
</table>
</ExtData>
I need write a function that by coreName return coreHref
It's mean ::
my_variable = same_func("SER") ==>> variable will be "../_a2.xml"
thank for help!

Here is XPath expression to select coreHref:
//Col/CoreCol[#coreEName='YorName']/data(#coreHref)

Define a key
<xsl:key name="by-name" match="Col/CoreCol" use="#coreEName"/>
then use it with e.g. key('by-name', 'SER')/#coreHref.
But note that inside of xsl:function you don't have a context node to apply the key function with two arguments, thus if you want to use the approach inside of an xsl:function make sure you have
<xsl:variable name="main-root" select="/"/>
as a global variable and then use key('by-name', 'SER', $main-root)/#coreHref.

Related

pre-processing script to switch product codes

I have a snippet of code I've inherited and I'm trying to get it to work on multiples of the match pattern and set a tag from looking up a value from a table using another tag. What happens is that, for every item, the same lookup is performed and not the relative one for the node. I can't work out the syntax to work thru all entries and substitute the correct one. It's got to be simple it's just that I am simpler :)
My source xml contains this (within an outer /oomsdoc document node not shown):
<item>
<lineqty> 1</lineqty>
<linesku>BNLP5008 </linesku>
<linecustprod>xxxxxxxxxxxxxxx</linecustprod>
<linedesc>London Pride (Bot500mlx8) </linedesc>
</item>
<item>
<lineqty> 1</lineqty>
<linesku>BNBL5008 </linesku>
<linecustprod>xxxxxxxxxxxxxxx</linecustprod>
<linedesc>Bengal Lancer (Bot500mlx8) </linedesc>
</item>
I want to substitute the xxxxxxxxxxxxxxx in each linecustprod tag with the material from the lookup table using the value of the linesku tag.
This is my lookup table:
<Materials>
<product sku='BNLP5008 ' material='LONDON PRIDE'/>
<product sku='BNBL5008 ' material='BENGAL LANCER'/>
</Materials>
and this is my xslt code.
<xsl:variable name="SkuList" select="document('d:\test\transforms\catalogue.xml')/Materials"/>
<xsl:template match="/oomsdoc/item/linecustprod">
<xsl:variable name="MySku" select="/oomsdoc/item/linesku"/>
<linecustprod>
<xsl:value-of select="$SkuList/product[#sku=$MySku]/#material"/>
</linecustprod>
</xsl:template>
I'm guessing some kind of xsl foreach would work but just can't find a usable example to crib :)
Your guidance again would be appreciated at this point in my frustration :)
Thanks,
Brian.
Changing the variable definition to
<xsl:variable name="MySku" select="../linesku"/>
should be sufficient, this will pull out the linesku that is a sibling to the linecustprod you're currently looking at. As currently defined the variable will contain a node set of all the linesku elements in the document, so the value-of will give you the first entry from $SkuList that matches any entry in the main input file.
In addition to Ian Roberts' answer, please change
<xsl:variable name="SkuList" select="document('d:\test\transforms\catalogue.xml')/Materials"/>
to
<xsl:variable name="SkuList" select="document('/d:\test\transforms\catalogue.xml')/Materials"/>
for some reason, the first throws an error (malformed URL).

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>

why this Apply template was not working properly

if suppose my database having three row records by fetching that data i am converting into xml data as shown below :
Here is my XML:
<NewDataSet>
<Table>
<RECORD_TYPE_CODE>CTD</RECORD_TYPE_CODE>
<MSG_TYPE_CODE>O103N</MSG_TYPE_CODE>
<CTD_SEQ_NUM>000086</CTD_SEQ_NUM>
<CTD_CTD_PKG_ID>2</CTD_CTD_PKG_ID>
</Table>
<Table>
<RECORD_TYPE_CODE>CTO</RECORD_TYPE_CODE>
<MSG_TYPE_CODE>O203N</MSG_TYPE_CODE>
<CTD_SEQ_NUM>000087</CTD_SEQ_NUM>
<CTD_CTD_PKG_ID>2</CTD_CTD_PKG_ID>
</Table>
<Table>
<RECORD_TYPE_CODE>CTH</RECORD_TYPE_CODE>
<MSG_TYPE_CODE>O303N</MSG_TYPE_CODE>
<CTD_SEQ_NUM>000088</CTD_SEQ_NUM>
<CTD_CTD_PKG_ID>2</CTD_CTD_PKG_ID>
</Table>
</NewDataSet>
through c sharp code behind using for loop i m sending package id (CTD_CTD_PKG_ID)and Sequencenum(CTD_SEQ_NUM) through xslt argument parameters it was passing properly in a sequential order like 000086,000087,000088 but first time for in aloop i will pass 000086 seqnum it was fetching proper manner next when i m passing 000087 it was generating data in table of 000086 only why it was happening passing parameter value is goin good in a sequential order
here is my XSLT:
<xsl:param name="PackageId" />
<xsl:param name="SequenceNum" />
<xsl:template match="/">
<xsl:apply-templates mode="SequenceB" select="(NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId] | NewDataSet/Table[CTD_SEQ_NUM =$SequenceNum])[1]"/>
</xsl:template>
I guess (if my guess is incorrect, you need to explain better the problem) that the value of your $PackageId parameter is always defined as 2.
The XPath expression you are using:
(NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId]
|
NewDataSet/Table[CTD_SEQ_NUM =$SequenceNum]
)
[1]
selects the first node from the union of the nodes selected by:
NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId]
and:
NewDataSet/Table[CTD_SEQ_NUM =$SequenceNum]
Do note, that if the value of the $PackageId parameter is 2, then the first node of theis union is always /*/Table[1].
The solution is to replace the XPath expression you are using with:
NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][CTD_SEQ_NUM =$SequenceNum]

XSL for-each filtering; how to evaluate TWO variable in select?

<xsl:variable name="filterValue"
>#subject = 'Subject To Filter On'</xsl:variable>
<xsl:for-each select="$Rows[$filterValue]">
It seems to me that the $filterValue is being treated as a literal string rather than being evaluated. How can I make it so that the variable's value is evaluated instead?
Desired result:
<xsl:for-each select="$Rows[#subject = 'Subject To Filter On']">
I apologize if this has been answered before, I don't know much terminology about XSL so I'm having trouble searching online. Thanks.
Dynamic evaluation of XPath expressions is not supported in both XSLT 1.0 and XSLT 2.0.
Usually, there are workarounds.
In this specific case you can have (global/external) parameters:
<xsl:param name="attrName" select="'subject'"/>
<xsl:param name="attrValue" select="'Subject To Filter On'"/>
and then in your code:
<xsl:for-each select="$Rows[#*[name()=$attrName] = $attrValue]">

XSLT: deep child copy

My need: I want to deep copy all the childs of a single selected node without actually copying it. Example: from
<father><son i="1" /><son i="2" /><son i="0"><lastNode /></son></father>
i wish to extract
<son i="1" /><son i="2" /><son i="0"><lastNode /></son>
I know that i can do this with a cycle for-each and then a xsl:copy-of. I am wondering if there is a simpler expression to achieve the same result. Some idea?
Follow-up. My question missed a couple of points. I should had said that all the childs means "all the possible childs", including textnodes; another verification that a better question already contains the answer. Second, what I have learned from you - the community - is that I was enough dumb to try to solve by XSL what in facts was more a XPATH issue. Thanks to all of you for this insight
Cheers.
Try select all children..
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:copy-of select="father/*"/>
</xsl:template>
</xsl:stylesheet>
E.G.
Given input
<father><son i="1" /><son i="2" /><niceSon /><son i="0"><lastNode /></son></father>
It outputs
<son i="1" /><son i="2" /><niceSon /><son i="0"><lastNode /></son>
<xsl:copy-of select="father/node()" />
Use e.g. <xsl:copy-of select="father/son"/>