msxsl:node-set() not recognized - xslt

I am trying to pull nodes out of a node set stored in a variable using the msxsl:node-set() function and am not getting anything. My xml looks like this:
<Root>
<Items olditemnumber="100" newitemnumber="200">
<Item ItemNumber="100" ItemAliasCode="1001" ItemCode="X" />
<Item ItemNumber="100" ItemAliasCode="1002" ItemCode="X" />
<Item ItemNumber="200" ItemAliasCode="2001" ItemCode="X" />
<Item ItemNumber="200" ItemAliasCode="2003" ItemCode="X" />
<Item ItemNumber="100" ItemAliasCode="1003" ItemCode="P" />
<Item ItemNumber="100" ItemAliasCode="1004" ItemCode="P" />
<Item ItemNumber="200" ItemAliasCode="2002" ItemCode="P" />
</Items>
</Root>
In my xslt I try to populate a variable with a subset of the nodes and then call them using the msxsl:node-set() function. This doesn't return anything however.
XSLT looks like this:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="//Root">
<xsl:variable name="OldItemNumber" select="/Items/#olditemnumber"/>
<xsl:variable name="NewItemNumber" select="/Items/#newitemnumber"/>
<xsl:variable name="OldItems">
<xsl:value-of select="//Item[#ItemNumber = $OldItemNumber]"/>
</xsl:variable>
<xsl:variable name="NewItems">
<xsl:value-of select="//Item[#ItemNumber = $NewItemNumber]"/>
</xsl:variable>
<xsl:for-each select="msxsl:node-set($OldItems)/Item">
...work
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The XSLT skips over the for-each loop, though I see in the watch that the the Xpath query grabs the right nodes in assigning the variables. The watch also tells me that the msxsl:node-set() function is undefined. Any help would be appreciated. What am I missing?

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:template match="//Root">
<xsl:variable name="OldItemNumber" select="/Items/#olditemnumber"/>
<xsl:variable name="NewItemNumber" select="/Items/#newitemnumber"/>
<xsl:variable name="OldItems" select="//Item[#ItemNumber = $OldItemNumber]"/>
<xsl:variable name="NewItems" select="//Item[#ItemNumber = $NewItemNumber]"/>
<xsl:for-each select="$OldItems">
...work
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
msxsl:node-set is for converting a result tree fragment (a.k.a. RTF) to a node set, which is not needed on your case.
xsl:value-of is for creating text nodes, so don't use it for selecting nodes of the input tree that you want to further query/process.

Related

How to determine the value format is dd-mmm-yyyy in xslt

I have to determine the input value having date format of dd-mmm-yyyy. If I can find will set some attribute based on the attribute I can do the format in C# report processing class.
<td>
<xsl:if test="To write expression to match the value">
<r>
<xyz:value-of select="'Set Value'" />
</r>
</xsl:if>
</td>
Input value is "30-Jun-2019". If it matches I want to set .
Basically I have set of columns in the report. I have to identify the the values in the report if the value matches with the Date format of dd-mmm-yyy setting some attribute in the xslt and applying the same format in report parser code which is written in c#
As I said in comments, there is no regex support in XSLT 1.0, so this can get quite tedious.
Consider the following example:
XML
<input>
<item>21-Jan-1987</item>
<item>921-Jan-1987</item>
<item>15-Jul-2009</item>
<item>15-Jux-2009</item>
<item>03-Dec-2014</item>
<item>03-Dec-999</item>
</input>
XSLT 1.0
<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:strip-space elements="*"/>
<xsl:template match="/input">
<output>
<xsl:for-each select="item">
<item value="{.}">
<xsl:variable name="dd" select="substring-before(., '-')" />
<xsl:variable name="mmm" select="substring-before(substring-after(., '-'), '-')" />
<xsl:variable name="yyyy" select="substring-after(substring-after(., '-'), '-')" />
<xsl:if test="translate($dd, '123456789', '000000000') = '00' and translate($yyyy, '123456789', '000000000') = '0000' and ($mmm='Jan' or $mmm='Feb' or $mmm='Mar' or $mmm='Apr' or $mmm='May' or $mmm='Jun' or $mmm='Jul' or $mmm='Aug' or $mmm='Sep' or $mmm='Oct' or $mmm='Nov' or $mmm='Dec')">
<xsl:text>Is Date</xsl:text>
</xsl:if>
</item>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<item value="21-Jan-1987">Is Date</item>
<item value="921-Jan-1987"/>
<item value="15-Jul-2009">Is Date</item>
<item value="15-Jux-2009"/>
<item value="03-Dec-2014">Is Date</item>
<item value="03-Dec-999"/>
</output>
Note that this checks only that the input conforms to the pattern, not that the date itself is valid. Also keep in mind that XML is case-sensititve.
Added:
If you prefer, you could simplify the test to:
<xsl:if test="translate(translate(translate(., '123456789', '000000000'), 'JFMASOND', '########'), 'anebpryulgctov', '%%%%%%%%%%%%%%') = '00-#%%-0000'">
but then a value like 15-Jpt-2009 will pass as date.
In XSLT 2.0 this is fairly trivial: matches(., '[0-9]{2}-[A-Z][a-z]{2}-[0-9]
{4}')
In 1.0 it's considerably harder, and it depends a little bit how precise you want to be. But you could get close with translate(translate($input, 'ABC...abc...', 'AAAAAAAA....'), '0123456789', '9999999999') = '99-AAA-9999') where the '...' means you have to write out the rest of the alphabet.

How do I generate a XSLT stylesheet for the following TEI snipet?

i wanted to ask you for help. I am totally new to XSLT and i wanted to know if someone can show me the right XSLT stylesheet for the following TEI-snipet:
<div>
<head>Weitere Aufzählungen</head>
<list rend="numbered">
<item n="1">A</item>
<item n="2">B</item>
<item n="3">C<list rend="numbered">
<item n="3.1">a</item>
<item n="3.2">b</item>
</list>
</item>
</list>
</div>
The Output should look like that in the HTML-Document:
1. A
2. B
3. C
3.1 a
3.2 b
Thank you so much for helping me :)
If you want a text output, the following stylesheet with <xsl:output method="text" /> will do. It distinguishes the level of indentation by counting the item ancestor nodes and adds an extra . on level 0.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes" />
<xsl:variable name="newLine" select="'
'" />
<xsl:template match="text()" />
<xsl:template match="/div/list">
<xsl:apply-templates select="item" />
</xsl:template>
<xsl:template match="item">
<xsl:for-each select="ancestor::item"><xsl:text> </xsl:text></xsl:for-each>
<xsl:value-of select="#n" />
<xsl:if test="not(ancestor::item)"><xsl:text>.</xsl:text></xsl:if>
<xsl:value-of select="concat(' ',text(),$newLine)" />
<xsl:apply-templates select="list" />
</xsl:template>
</xsl:stylesheet>
Output is:
1. A
2. B
3. C
3.1 a
3.2 b
BTW you may need to add an appropriate namespace declaration for the TEI namespace to the xsl:stylesheet element.

How to count created node XSL?

I was asking myself a question.
I'm on project where I've to transform a .XML file into an other one (after a treatment) but I've to do a numbered list. I know the count(//node) function but I don't think we can count the created nodes with.
For example this is what my .xsl looks like :
<xsl:template match="/">
<Type>
<List>
<xsl:apply-templates mode="PartOne" select="/Stuff/Info/TypeA"/>
<xsl:apply-templates mode="PartOneBis" select="/Stuff/Info/TypeB"/>
</List>
<AnotherList>
<xsl:apply-templates mode="PartTwo" select="/Stuff/Info/TypeB"/>
</AnotherList>
</Type>
</xsl:template>
<xsl:template mode="PartOne" match="/Stuff/Info/TypeA">
<PartOne indexlist="{position()-1}">
... treatment ...
</PartOne>
</xsl:template>
<xsl:template mode="PartOneBis" match="/Stuff/Info/TypeB">
<xsl:if test="TypeB_Indice != 'stuff'">
<PartOne indexlist="{count(//TypeA) + position()-1}">
... treatment ...
</PartOne>
</xsl:if>
</xsl:template>
<xsl:template mode="PartTwo" match="/Stuff/Info/TypeB">
<xsl:if test="TypeB_Indice = 'stuff'">
<PartTwo indexlist="{count(//TypeA) + position()-1}">
... treatment ...
</PartTwo>
</xsl:if>
</xsl:template>
And this is what my .xml looks like:
<Stuff>
<Info>
<TypeA>
<TypeA_Stuff/>
<TypeA_Indice>xxx</TypeA_Indice>
</TypeA>
<TypeB>
<TypeB_Stuff/>
<TypeB_Indice>xxx</TypeB_Indice>
</TypeB>
</Info>
</Stuff>
---------------------- edit ----------------------------
The conditions for the PartOneBis is more complacated than the one I put in this code, there's more like 6 differents factor that can change its state from ok to not ok.
I was thinking of a for-each with an if and an incrementation but this don't work because you can't overwrite a variable or may be I'm wrong in my method.
If there's a way to count the node create before your point without having to create two .xml or use a c++ function I'll like to know it.
Thanks.
I need to put the "partOne" type first and the "partTwo" second but in the xml I've got, there's some conditions that makes that the TypeB is a partOne otherwise the other cases'll be a partTwo.
TypeA -> partOne
TypeB -> if (something) partOne else partTwo
But the condition depend of several value which don't came from the same node but the wanted result is something like that
<PartOne indexlist="0">
SomeStuff
</PartOne>
<PartOne indexlist="1">
SomeStuff
</PartOne>
<PartTwo indexlist="2">
SomeStuff
</PartTwo>
I find it impossible to follow your example. Consider a simpler one:
XML
<input>
<item>red</item>
<item>red red</item>
<item>red blue</item>
<item>red green</item>
<item>blue</item>
<item>blue blue</item>
<item>blue green</item>
<item>blue red</item>
<item>green</item>
</input>
XSLT 1.0
<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:strip-space elements="*"/>
<xsl:template match="/input">
<xsl:variable name="red-items" select="item[contains(., 'red')]" />
<xsl:variable name="blue-items" select="item[contains(., 'blue')]" />
<output>
<red-list>
<xsl:apply-templates select="$red-items"/>
</red-list>
<blue-list>
<xsl:apply-templates select="$blue-items">
<xsl:with-param name="n" select="count($red-items)"/>
</xsl:apply-templates>
</blue-list>
</output>
</xsl:template>
<xsl:template match="item">
<xsl:param name="n" select="0"/>
<item i="{$n + position()}">
<xsl:value-of select="." />
</item>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<red-list>
<item i="1">red</item>
<item i="2">red red</item>
<item i="3">red blue</item>
<item i="4">red green</item>
<item i="5">blue red</item>
</red-list>
<blue-list>
<item i="6">red blue</item>
<item i="7">blue</item>
<item i="8">blue blue</item>
<item i="9">blue green</item>
<item i="10">blue red</item>
</blue-list>
</output>

XSLT Appending incremented value to existing attribute value

For my input XML, I have written the XSLT , But I cannot make the XSLT to generate the <mynewtag> correctly. Please help.
XML input:
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book.child.1>
<title>charithram</title>
<author>sarika</author>
</book.child.1>
<book.child.2>
<title>doublebell</title>
<author>psudarsanan</author>
</book.child.2>
</books>
Expected Output:
<?xml version="1.0" encoding="UTF-8"?>
<newbooks>
<newbook>
<mynewtag id="book1" />
<title>charithram</title>
<author>sarika</author>
</newbook>
<newbook>
<mynewtag id="book2" />
<title>doublebell</title>
<author>psudarsanan</author>
</newbook>
</newbooks>
XSLT that I tried: [I understand the syntax is incorrect for <mynewtag> ]. But I don't know to fix it to get the desired output.
<?xml version="1.0" encoding="ISO-8859-1"?>
<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="/">
<newbooks>
<xsl:for-each select="books/child::*">
<newbook>
<mynewtag id="book<xsl:number value='position()' format='1' />" />
<title>
<xsl:value-of select="title" />
</title>
<author>
<xsl:value-of select="author" />
</author>
</newbook>
</xsl:for-each>
</newbooks>
</xsl:template>
</xsl:stylesheet>
Trying in Online XSLT transformer , http://www.freeformatter.com/xsl-transformer.html
I tried assigning the position to a variable, but still I face the same problem of not knowing how to append it with the attribute value book .
<xsl:variable name="cnt">
<xsl:number value='position()' format='1' />
</xsl:variable>
<xsl:value-of select = "$cnt" />
Note: If I remove the <xsl:number value='position()' format='1' /> from XSL, then the syntax is correct, but then I won't be able to generate book1 book2 etc as <mynewtag> attribute values.
Please help.
Added: <mynewtag> is a required element. It is like any other XML element like <title> that is required in output. It is not an element just to hold the attribute id. Sorry if there is a confusion on this.
Adding Solution here, from the answers obtained to summarize:
<mynewtag>
<xsl:attribute name="id">
<xsl:text>book</xsl:text>
<xsl:number value='position()'/>
</xsl:attribute>
</mynewtag>
or shortly:
<mynewtag id="book{position()}" />"
or
<newbook>
<xsl:variable name="cnt">
<xsl:number value='position()' format='1' />
</xsl:variable>
<mynewtag id="book{$cnt}" />
..........
Also the attribute value templates that IanRoberts mentioned.
You can't place a tag inside another tag. Try either:
<mynewtag>
<xsl:attribute name="id">
<xsl:text>book</xsl:text>
<xsl:number value='position()'/>
</xsl:attribute>
</mynewtag>
or shortly:
<mynewtag id="book{position()}" />"
ADDED:
Not directly related to your question, but I believe the id attribute should be applied to the parent <newbook> element, instead of creating an artificial child element to hold it.

XPath relative path in expression

I am in 'group' node. From it, I want to find such 'item' node, that has 'id' attribute equals to current's 'group' node 'ref_item_id' attribute value. So in my case, by being in 'group' node B, I want 'item' node A as output. This works:
<xsl:value-of select="preceding-sibling::item[#id='1']/#description"/>
But this doesn't (gives nothing):
<xsl:value-of select="preceding-sibling::item[#id=#ref_item_id]/#description"/>
When I type:
<xsl:value-of select="#ref_item_id"/>
I have '1' as result. So this attribute is for sure accessible, but I can't find path to it from XPath expression above. I tried many '../' combinations, but couldn't get it work.
Code to test: http://www.xmlplayground.com/7l42fo
Full XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item description="A" id="1"/>
<item description="C" id="2"/>
<group description="B" ref_item_id="1"/>
</root>
Full XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:template match="root">
<xsl:for-each select="group">
<xsl:value-of select="preceding-sibling::item[#id=#ref_item_id]/#description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This has to do with context. As soon as you enter a predicate, the context becomes the node currently being filtered by the predicate, and no longer the node matched by the template.
You have two options - use a variable to cache the outer scope data and reference that variable in your predicate
<xsl:variable name='ref_item_id' select='#ref_item_id' />
<xsl:value-of select="preceding-sibling::item[#id=$ref_item_id]/#description"/>
or make use of the current() function
<xsl:value-of select="preceding-sibling::item[#id=current()/#ref_item_id]/#description"/>
Your expression searches for an item whose id attribute matches its own ref_item_id. You need to capture the current ref_item_id in an xsl:variable and refer to that xsl:variable in the expression.
One more possible solution using xsl:key
<xsl:key name="kItemId" match="item" use="#id" />
<xsl:template match="root">
<xsl:for-each select="group">
<xsl:value-of select="key('kItemId', #ref_item_id)[1]/#description"/>
</xsl:for-each>
</xsl:template>
Looking at the XML, if I assume that you have <item> and <group> as siblings and in any order.
Then a sample input XML would look like the following.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item description="A" id="1"/>
<item description="C" id="2"/>
<group description="B" ref_item_id="1"/>
<item description="D" id="1"/>
<group description="E" ref_item_id="2"/>
</root>
Now, if the goal is to extract the description of all the <item> nodes whose id is matching with corresponding <group> *nodes ref_item_id*. Then we can simply loop over only such <item> nodes and get their description.
<xsl:output method="text" indent="no"/>
<xsl:template match="root">
<xsl:for-each select="//item[(./#id=following-sibling::group/#ref_item_id) or (./#id=preceding-sibling::group/#ref_item_id)]">
<xsl:value-of select="./#description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Since you say that nodes are having unique id and all nodes are placed before nodes.
I would recommend you to use the following XSL and loop over specific nodes instead of nodes.
<xsl:output method="text" indent="no"/>
<xsl:template match="root">
<xsl:for-each select="//item[./#id=following-sibling::group/#ref_item_id]">
<xsl:value-of select="./#description"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>