I have an xml like this one:
<?xml version="1.0" encoding="WINDOWS-1252" ?>
<?xml-stylesheet type="text/xsl" href="test.xsl"?>
<root>
<something>
<cd>a1</cd>
<cd>a2</cd>
</something>
<another>
<cd>b1</cd>
<cd>b2</cd>
<cd>b3</cd>
<cd>b1</cd>
<cd>b2</cd>
<cd>b3</cd>
<cd>b1</cd>
<cd>b2</cd>
</another>
</root>
I'm selecting all the cd nodes and trying to take the cds from 10 to 10 (1st, 11th, 21th...) this way:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html><body>
<xsl:apply-templates select="//cd" />
</body></html>
</xsl:template>
<xsl:template match="cd"/>
<xsl:template match="cd[position() mod 10 = 1]">
<div>
pos:<xsl:value-of select="position()"/>;
value:<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:stylesheet>
The output:
pos:1; value:a1
pos:3; value:b3
And the expected output:
pos:1; value:a1
pos:11; value:b11
The problem is that position() in the match is the node number based on his parent. So the position() of "b1" is 1 and not 3. Is there a function to know the index of the current node?
Thanks in advance.
//EDIT to extend the example
You can get a more accurate count by using xsl:number:
<xsl:template match="cd">
<div>pos: <xsl:number level="any"/></div>
</xsl:template>
You could also count the preceding cd elements:
<xsl:template match="cd">
<div>pos: <xsl:value-of select="count(preceding::cd)+1"/></div>
</xsl:template>
I prefer xsl:number, but I suppose it depends on how you're using the value. If you give a better example of what you're trying to do with that number, I can update my answer.
Example based on question update:
Input
<root>
<something>
<cd>a1</cd>
<cd>a2</cd>
</something>
<another>
<cd>b1</cd>
<cd>b2</cd>
<cd>b3</cd>
<cd>b1</cd>
<cd>b2</cd>
<cd>b3</cd>
<cd>b1</cd>
<cd>b2</cd>
</another>
<another>
<cd>c1</cd>
<cd>c2</cd>
<cd>c3</cd>
<cd>c1</cd>
<cd>c2</cd>
<cd>c3</cd>
<cd>c1</cd>
<cd>c2</cd>
</another>
</root>
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html><body>
<xsl:apply-templates select="//cd" />
</body></html>
</xsl:template>
<xsl:template match="cd"/>
<xsl:template match="cd[(count(preceding::cd)+1) mod 10 = 1]">
<div>
pos:<xsl:value-of select="position()"/>;
value:<xsl:value-of select="."/>
</div>
</xsl:template>
</xsl:stylesheet>
Output
<html>
<body>
<div>
pos:1;
value:a1
</div>
<div>
pos:11;
value:c1
</div>
</body>
</html>
The position() in the match of template filters "cd"s based on position in its parent:
<xsl:template match="cd[position() mod (10) = 1]">
and the position() inside the template definition,
<div>pos:<xsl:value-of select="position()"/></div>
gives you the cd's sequence which the template has been applied for.
Thus, "position() mod (10) = 1" filters only those "cd"s which are 1st, 11th, 21st, etc. children of their parents.
Hence, you can remove the filter from xsl:template's match and put it inside like this:
<xsl:template match="cd">
<div>pos:<xsl:if test="position() mod 10 = 1">
<xsl:value-of select="position()"/>
</xsl:if>
</div>
</xsl:template>
This will save all the CDs in a variable. In the for-each, the context does not depend on the parent nodes so you will get the position among all CDs:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:variable name="all-cds" select="//cd" />
<xsl:template match="root">
<xsl:for-each select="$all-cds">
<div>pos: <xsl:value-of select="position()"/></div>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Related
I coded the XSLT to copy one node data to another by validating the attribute value, I got the desired output but I'm curious to know whether there is an efficient way to do this or if this is the only way to do it. [I'm not an XSLT expert] Can someone help !!!
Please use this link to check instantly.
https://xsltfiddle.liberty-development.net/pNvtBH2/3
Actual XML:
<?xml version="1.0" encoding="utf-8" ?>
<section>
<p>note 1 : 1</p>
<p>note 2 : 2</p>
<p>note 3 : 3</p>
<note id="test1">hello one</note>
<note id="test2">hello two</note>
<note id="test3">hello <i>three</i></note>
<note id="test4">hello <i>four</i></note>
</section>
Output:
<?xml version="1.0" encoding="UTF-8"?><section>
<p>note 1 : <a>hello one</a></p>
<p>note 2 : <a>hello two</a></p>
<p>note 3 : <a>hello <i>three</i></a></p>
<note id="test4">hello <i>four</i></note>
</section>
XSLT Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a">
<a>
<xsl:variable name="href" select="#href" />
<xsl:choose>
<xsl:when test="$href = //note/#id">
<xsl:copy-of select="//note[#id=$href]/node()" />
</xsl:when>
</xsl:choose>
</a>
</xsl:template>
<xsl:template match="note">
<xsl:choose>
<xsl:when test="#id = //a/#href">
<xsl:apply-templates select="node" />
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Whether it's efficient or not depends on how smart the optimizer in your XSLT processor is. Saxon-EE will do a pretty good job on this, Saxon-HE less so.
If you want to make it efficient on all processors, use keys. Replace an expression like //note[#id=$href] with a call on the key() function. Declare the key as
<xsl:key name="k" match="note" use="id"/>
and then you can get the matching nodes using key('k', $href).
The xsl:when test="$href = //note/#id" is redundant, as xsl:copy-of will do nothing if nothing is selected.
In the note template, I'm not sure what
<xsl:when test="#id = //a/#href">
<xsl:apply-templates select="node" />
</xsl:when>
is trying to achieve, because you don't have any elements named "node". If the aim is to avoid processing a note element at this stage if it was already processed from an a element, then you could build another index with
<xsl:key name="a" match="a" use="1"/>
and replace test="#id = //a/#href" with test="key('a', #href)"
<a>
<z/>
<b/>
<b/>
<b/>
<c/>
</a>
I want to find the count of 'b' within 'a' when my parsing current node is 'c' using XSLT.
Is it possible to do this using XSLT?
I am not aware of what the element name 'b' would be, i.e. for its preceding sibling.
If you are positioned on c tag, or whatever the element is actually called, then to get the count of the preceding siblings, you would do this...
<xsl:value-of select="count(preceding-sibling::*)" />
EDIT: In answer to your comment, if you don't want to count all siblings, but only the count of the immediately preceding one, and the ones with the same name before that, you could try this...
<xsl:value-of select="count(preceding-sibling::*[name() = name(current()/preceding-sibling::*[1])])" />
This would not work though in the case you had multiple c nodes under one parent...
<a>
<z/>
<b/>
<b/>
<b/>
<c/>
<z/>
<b/>
<c/>
</a>
In this case, you could define a key like this, to group elements by the unique id of the first following element with a different name:
<xsl:key name="keyc" match="*" use="generate-id(following-sibling::*[name() != name(current())][1])" />
Then you can get the count like so:
<xsl:value-of select="count(key('keyc', generate-id()))" />
Here are the three options in action....
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="keyc" match="*" use="generate-id(following-sibling::*[name() != name(current())][1])" />
<xsl:template match="c">
<c>
<example1>
<xsl:value-of select="count(preceding-sibling::*)" />
</example1>
<example2>
<xsl:value-of select="count(preceding-sibling::*[name() = name(current()/preceding-sibling::*[1])])" />
</example2>
<example3>
<xsl:value-of select="count(key('keyc', generate-id()))" />
</example3>
</c>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Use xsl:number. It prints out the numer of the current element, formatted as required.
There are various options concerning how to perform the numeration,
e.g. multi-level or alphabetic one.
Actually it is quite a powerful tool.
I want to find the count of 'b' within 'a' when my parsing current node is 'c'
Let me rephrase that:
you want to count all occurrences of <b> which are on the same level as <c>.
This XSLT does the job by calling an <xsl:template> with a parameter:
the local-name of the element to be counted (in this case 'b'):
<?xml version="1.0"?>
<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="//c"> <!-- select the desired target element -->
<xsl:call-template name="count">
<xsl:with-param name="elemName" select="'b'" /> <!-- name of the element -->
</xsl:call-template>
</xsl:template>
<xsl:template name="count"> <!-- count all matching elements before and after -->
<xsl:param name="elemName" />
<xsl:value-of select="count(preceding-sibling::*[local-name() = $elemName]) + count(following-sibling::*[local-name() = $elemName])" />
</xsl:template>
</xsl:stylesheet>
In the case of your example the output simply is:
3
I want to get the first heading (h1) before a table in a docx.
I can get all headings with:
<xsl:template match="w:p[w:pPr/w:pStyle[#w:val='berschrift1']]">
<p>
<context>
<xsl:value-of select="." />
</context>
</p>
</xsl:template>
and I can also get all tables
<xsl:template match="w:tbl">
<p>
<table>
<xsl:value-of select="." />
</table>
</p>
</xsl:template>
But unfortunetly the processor does not accept
<xsl:template match="w:tbl/preceding-sibling::w:p[w:pPr/w:pStyle[#w:val='berschrift1']]">
<p>
<table>
<xsl:value-of select="." />
</table>
</p>
</xsl:template>
Here is a reduced XML file extracted from a docx: http://pastebin.com/KbUyzRVv
I want something like that as a result:
<context>Let’s get it on</context> <- my heading
<table>data</table>
<context>Let’s get it on</context> <- my heading
<table>data</table>
<context>We’re in the middle of something</context> <- my heading
<table>data</table>
Thanks to Daniel Haley I was able to find a solution for that problem. I'll post it here, so it is independend of the pastebin I postet below.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:v="urn:schemas-microsoft-com:vml" exclude-result-prefixes="xsl w v">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="w:tbl">
<context>
<xsl:value-of select="(preceding-sibling::w:p[w:pPr/w:pStyle[#w:val = 'berschrift1']])[last()]"/>
</context>
<table>
<xsl:value-of select="."/>
</table>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
Hard to answer without a Minimal, Complete, and Verifiable example, but try this:
<xsl:template match="w:tbl">
<p>
<table>
<xsl:value-of select="(preceding::w:p[w:pPr/w:pStyle[#w:val='berschrift1']])[last()]"/>
</table>
</p>
</xsl:template>
Assuming you can use XSLT 2.0 (and most people can, nowadays), I find a useful technique here is to have a global variable that selects all the relevant nodes:
<xsl:variable name="special"
select="//w:tbl/preceding-sibling::w:p[w:pPr/w:pStyle[#w:val='berschrift1']][1]"/>
and then use this variable in a template rule:
<xsl:template match="w:p[. intersect $special]"/>
In XSLT 3.0 you can reduce this to
<xsl:template match="$special"/>
I have the below xml text,
<SUBSCRIBER>
<Anumber>639081000000</Anumber>
<FirstCallDate>20130430104419</FirstCallDate>
<SetyCode>TNT</SetyCode>
<Status>ACT</Status>
<RoamIndicator/>
<PreloadCode>P1</PreloadCode>
<CreationDate>20130116100037</CreationDate>
<PreActiveEndDate/>
<ActiveEndDate>20130804210541</ActiveEndDate>
<GraceEndDate>20140502210541</GraceEndDate>
<RetailerIndicator/>
<OnPeakAccountID>9100</OnPeakAccountID>
<OnPeakSmsExpDate>20130804210504</OnPeakSmsExpDate>
<UnivWalletAcc/>
<UnliSmsOnCtl>20130606211359</UnliSmsOnCtl>
<UnliSmsTriCtl/>
<UnliSmsGblCtl/>
<UnliMocOnCtl>20130606211359</UnliMocOnCtl>
<UnliMocTriCtl/>
<UnliMocGblCtl/>
<UnliSurfFbcCtl>20130606212353</UnliSurfFbcCtl>
</SUBSCRIBER>
How can I iterate/parse over each xml tag name and get the value ( I need the name of the tag and value in a different variables) ? And also, How can I start iterating from particular tag name ? Ex: I would like to start iterating UnivWalletAcc
Please advise.
So far, I have tried the following,
<xsl:template match="SUBSCRIBER">
<xsl:variable name="tagName">
<xsl:value-of select="/*/*/name()"/>
</xsl:variable>
<xsl:variable name="tagValue">
<xsl:value-of select="/*/*/text()"/>
</xsl:variable>
<xsl:value-of select="$tagName"/>
<xsl:value-of select="$tagValue"/>
</xsl:template>
As an alternative to Veenstra's solution, instead of the xsl:if in the SUBSCRIBER/* template, you can control the iteration in the apply-templates:
<xsl:template match="SUBSCRIBER">
<data>
<xsl:apply-templates select="UnivWalletAcc,
UnivWalletAcc/following-sibling::*" />
</data>
</xsl:template>
With the following XSLT you can loop through all childs of the node SUBSCRIBER:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Identity template that will loop over all nodes and attributes -->
<xsl:template match="#*|node()">
<xsl:apply-templates select="#*|node()" />
</xsl:template>
<!-- Template to match the root and create new root -->
<xsl:template match="SUBSCRIBER">
<data>
<xsl:apply-templates select="#*|node()" />
</data>
</xsl:template>
<!-- Template to loop over all childs of SUBSCRIBER node -->
<xsl:template match="SUBSCRIBER/*">
<!-- This will test if we have seen the UnivWalletAcc node already, if so, output something, otherwise, output nothing -->
<xsl:if test="preceding-sibling::UnivWalletAcc or self::UnivWalletAcc">
<tag>
<tagName><xsl:value-of select="name()" /></tagName>
<tagValue><xsl:value-of select="." /></tagValue>
</tag>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I'm new to XSLT. I have a block code that I don't understand.
In the following block what does '*','*[#class='vcard']' and '*[#class='fn']' mean?
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" encoding="utf-8"/> <xsl:template match="/">
<script type="text/javascript">
<xsl:text><![CDATA[function show_hcard(info) {
win2 = window.open("about:blank", "HCARD", "width=300,height=200," + "scrollbars=no menubar=no, status=no, toolbar=no, scrollbars=no");
win2.document.write("<h1>HCARD</h1><hr/><p>" + info + "</p>"); win2.document.close();
}]]></xsl:text>
</script>
<xsl:apply-templates/> </xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy> </xsl:template>
<xsl:template match="*[#class='vcard']">
<xsl:apply-templates/> </xsl:template>
<xsl:template match="*[#class='fn']">
<u>
<a>
<xsl:attribute name="onMouseDown">
<xsl:text>show_hcard('</xsl:text>
<xsl:value-of select="text()"/>
<xsl:text>')</xsl:text>
</xsl:attribute>
<xsl:value-of select="text()"/>
</a>
</u> </xsl:template> </xsl:stylesheet>
* matches all elements, *[#class='vcard'] pattern matches all elements with class attribute of vcard value. From that you can figure out what *[#class='fn'] may mean ;-)
I'd also suggest that you start here.
Your stylesheet has four template rules. In English these rules are:
(a) starting at the top (match="/"), first output a script element, then process the next level down (xsl:apply-templates) in the input.
(b) the default rule for elements (match="*") is to create a new element in the output with the same name and attributes as the original, and to construct its content by processing the next level down in the input.
(c) the rule for elements with the attribute class="vcard" is to do nothing with this element, other than to process the next level down in the input.
(d) the rule for elements with the attribute class="fn" is to output
<u><a onMouseDown="show_hcard('X')">X</a></u>
where X is the text content of the element being processed.
A more experienced XSLT user would have written the last rule as
<xsl:template match="*[#class='fn']">
<u>
<a onMouseDown="show_hcard('{.}')">
<xsl:value-of select="."/>
</a>
</u>
</xsl:template>