I have 2 sequences one is from 1 to 10 and other is 1 to 3.
how to compare the second sequence with first sequence to test whether first 3 items are same ?
Use this short XPath expression:
deep-equal(subsequence($seq1, 1, count($seq2)), $seq2)
When evaluated it produces true() exactly if the second sequence is a starting subsequence of the first. The two sequences can contain a mixture of any XDM items, including nodes.
Here is a full demonstration:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="vSeq1" select=
"1, 2, 3, 4, 5, 6, 7, 8, 9, 10"/>
<xsl:variable name="vSeq2" select=
"1, 2, 3"/>
<xsl:variable name="vSeq3" select="/*/*"/>
<xsl:variable name="vSeq4" select="/*/*[position() gt 5]"/>
<xsl:variable name="vSeq5" select="/*/*"/>
<xsl:variable name="vSeq6" select="/*/*[position() le 5]"/>
<xsl:template match="/">
<xsl:sequence select=
"deep-equal(subsequence($vSeq1, 1, count($vSeq2)), $vSeq2)"/>
===========
<xsl:sequence select=
"deep-equal(subsequence($vSeq3, 1, count($vSeq4)), $vSeq4)"/>
===========
<xsl:sequence select=
"deep-equal(subsequence($vSeq5, 1, count($vSeq6)), $vSeq6)"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
true
===========
false
===========
true
Assuming you have two variables seq1 and seq2 you can simply test <xsl:if test="$seq1[1] eq $seq2[1] and $seq1[2] eq $seq2[2] and $seq1[3] eq $seq2[3]">...</xsl:if>.
Related
I have problem with XSLT and/or XPATH. Let's say I have XML Input:
<context>
<pdpid-set>
<list>
<item>1</item>
<item>2</item>
<item>4</item>
<item>6</item>
<item>7</item>
<item>8</item>
</list>
</pdpid-set>
</context>
Task is: find FIRST missing element in array pdpid-set/list. In example above answer is 3.
I tried to use <xsl:for-each to find missing element but there is no possibility to break such loop so my XSL produce more than one element in output:
<xsl:variable name="list" select="context/pdpid-set/list"/>
<xsl:variable name="length" select="count(context/pdpid-set/list/item)"/>
<xsl:for-each select="1 to ($length)">
<xsl:variable name="position" select="position()"/>
<xsl:if test="$list/item[$position] > $position">
<missing-value>
<xsl:value-of select="$position"/>
</missing-value>
</xsl:if>
</xsl:for-each>
in code above output will be:
<missing-value>3</missing-value><missing-value>4</missing-value><missing-value>5</missing-value>...
I don't want to have more than one missing-value. Any suggestion?
Even in XPath 1.0
/context
/pdpid-set
/list
/item[not(position()=.)][1]
Do note: this select the first item not aligned with the ascending order. I still think that position() is better than following-sibling axis performance wise and for code clarity. Also, it lets you easily change starting number and step like in:
/context
/pdpid-set
/list
/item[not((position() - 1) * $step + $start = .)][1]
Task is: find FIRST missing element in array pdpid-set/list. In
example above answer is 3
Here is a correct XPath 1.0 expression that when evaluates to the wanted result (3):
/*/*/*/item[not(. +1 = following-sibling::*[1])][1] + 1
The XPath expression in the currently selected answer, on the other side, selects this element:
<item>4</item>
And the complete correct XSLT 1.0 transformation is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<missing-value>
<xsl:copy-of select="/*/*/*/item[not(. +1 = following-sibling::*[1])][1] + 1"/>
</missing-value>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document, the wanted, correct result is produced:
<missing-value>3</missing-value>
Finally, if the task is to find all missing elements:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match=
"item[following-sibling::* and not(number(.) +1 = following-sibling::*[1]/number())]">
<xsl:for-each select="xs:integer(.) + 1 to following-sibling::*[1]/xs:integer(.) -1">
<missing-value><xsl:copy-of select="."/></missing-value>
</xsl:for-each>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when this XSLT 2.0 transformation is applied on the following XML document (missing 3, 5, and 6):
<context>
<pdpid-set>
<list>
<item>1</item>
<item>2</item>
<item>4</item>
<item>7</item>
<item>8</item>
</list>
</pdpid-set>
</context>
the wanted, correct result is produced:
<missing-value>3</missing-value>
<missing-value>5</missing-value>
<missing-value>6</missing-value>
I am transforming following XML to generate HTML.
XML
<clause code="section6">
<variable col="1" name="R1C1" row="1">Water</variable>
<variable col="2" name="R1C2" row="1">true</variable>
<variable col="1" name="R2C1" row="2">Gas</variable>
<variable col="2" name="R2C2" row="2"></variable>
<variable col="1" name="R3C1" row="3">Petrol</variable>
<variable col="2" name="R3C2" row="3">true</variable>
<clause>
XSLT
1: <xsl:for-each select="$clause/variable[#col='1']">
2: <xsl:sort select="#row" data-type="number"/>
3: <xsl:variable name="row-id" select="#row"/>
4: <xsl:variable name="row" select="$clause/variable[#row=$row-id]"/>
5: <xsl:if test="$clause/variable[#col='2' and #row=$row-id]='true'">
6: <xsl:value-of name="row-no" select="concat(position(), ') ')"/>
7: <xsl:value-of select="$clause/variable[#col='1' and #row=$row-id]"/>
8: </xsl:if>
9: </xsl:for-each>
The transformation works fine and shows result 1) Water 3) Petrol
The issue is sequence number. You can see condition on Line 5 filters rows that only have 'true' value in col 2 and position() used for displaying sequence number. I cannot have running counter in XLST.
I was wondering if I can add condition of Line 5 with for-each at Line 1. The result with above example should be 1) Water 2) Patrol any advice?
Does this do what you want?
I drive the for-each selection on col 2 being true. That way position, which equals where we are in the selected set of nodes will equal 2 not 3
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:for-each select="clause/variable[#col='2' and text()='true']">
<xsl:sort select="#row" data-type="number"/>
<xsl:variable name="row-id" select="#row"/>
<xsl:value-of select="concat(position(), ') ')"/>
<xsl:value-of select="/clause/variable[#col='1' and #row=$row-id]"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Though I'd probably use templates in preference to the for-each and use current() so that we don't need the row-id variable:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:apply-templates select="clause/variable[#col='2' and text()='true']">
<xsl:sort select="#row" data-type="number"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="clause/variable[#col='2' and text()='true']">
<xsl:value-of select="concat(position(), ') ')"/>
<xsl:value-of select="/clause/variable[#col='1' and #row=current()/#row]"/>
</xsl:template>
</xsl:stylesheet>
I don't think you can express this with a single XPath 1.0 expression.
In XSLT 1.0 I will use keys and the solution becomes short, elegant and efficient:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kVarCol2" match="variable[#col=2]" use="#row"/>
<xsl:template match="/*">
<xsl:for-each select="variable[#col='1'][key('kVarCol2', #row)='true']">
<xsl:sort select="#row" data-type="number"/>
<xsl:value-of select="concat('
', position(), ') ', .)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document (corrected to be made well-formed):
<clause code="section6">
<variable col="1" name="R1C1" row="1">Water</variable>
<variable col="2" name="R1C2" row="1">true</variable>
<variable col="1" name="R2C1" row="2">Gas</variable>
<variable col="2" name="R2C2" row="2"></variable>
<variable col="1" name="R3C1" row="3">Petrol</variable>
<variable col="2" name="R3C2" row="3">true</variable>
</clause>
the wanted, correct result is produced:
1) Water
2) Petrol
II. XPath 2.0 (single expression) solution:
for $i in 1 to max(/*/*/#row/xs:integer(.))
return
/*/variable[#row eq string($i)]
[#col eq '1'
and
../variable
[#row eq string($i)
and
#col eq '2'
and
. eq 'true'
]
]
/concat('
', position(), ') ', .)
When this XPath 2.0 expression is evaluated on the same XML document (above), the result is the same wanted string:
1) Water
2) Petrol
When iterating through the nodes in a node-set variable I want an XPATH 1.0 expression that returns all ancestors of a node (e.g., of $myVariable[7]) -- not the ancestors in the node-set variable, but ancestors in the original document.
I thought one of these would work, but neither does.
select="//*[generate-id()=generate-id($myVariable[7])]/ancestor::*"
select="id(generate-id($myVariable[7]))/ancestor::*"
Am I close?
Edit: It's not central to my question, but I had //ancestor; that extra slash is unnecessary.
Your expression:
//*[generate-id()=generate-id($myVariable[7])]/ancestor::*
is correct.
The reason it is "not working" may be due to the fact that $myVariable[7] doesn't contain what you are expecting.
Here is a simple complete example using the above expression and producing the expected. correct results:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="myVariable"
select="/*/*/*/*/num"/>
<xsl:template match="/">
<xsl:for-each select=
"//*[generate-id()
=
generate-id($myVariable[7])
]
/ancestor::*
">
<xsl:value-of select="name()"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<a>
<b>
<c>
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
</c>
</b>
</a>
the wanted, correct result (the names of all ancestors of $myVariable[7]) is produced:
a
b
c
nums
What is an example of a compound select statement using an AND operator, similar to an if statement where condition 1 = true and condition 2 = true?
Here is one of the simplest possible examples:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match=
"num[. mod 2 = 0 and . mod 3 = 0]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when this transformation is applied on this XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
<num>06</num>
Here is an example of how to use a compound select statement....
<?xml version="1.0" encoding="ISO-8859-1"?>
<A>
<B>
<C>1</C>
<D>2</D>
</B>
<B>
<C>1</C>
<D>3</D>
</B>
<E>test</E>
</A>
and your current template match is for "E", then try the below code to select only B where C = 1 and D = 3: for C and D read condition 1 = true and condition 2 = true
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" indent="yes"/>
<xsl:template match="E">
<xsl:value-of select="../B[C = 1][D = 3]"></xsl:value-of>
</xsl:template>
<xsl:template match="C"/>
<xsl:template match="D"/>
</xsl:stylesheet>
Good Luck
Use the AND operator:
<xsl:if test="a and b">
So here is my problem...
I have 2 xsl transformations and they both have xsl:for-each in them as a starting point.
I need to create one (master) xslt which will call them. But of course, there is a catch.
This new xslt should give output one node from first xslt, then one node from second.(both xslt have EmployeeId, but are basically different reports that have to be printed one after another).
Because these existing xslt's have for-each in them, when I include them in this master xslt I get an output: all nodes from first xslt, then all nodes from second.
Also this 2 xslt's have to be backward compatible, so they should work as before if they are not called from this master template.
I'm a starter to xslt and I managed to create some reports when working with only one xslt,but I can't seem to find the solution to this problem, so I appreciate all the help I can get.
I was thinking of creating a new xslt that would be a mix of two that I have, but this was ruled out by my boss.
Thanks,
Benxy
EDIT:
Here goes some examples as requested:
This is the first xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" version="4.0" encoding="windows-1250" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/" name="testXslt1">
<xsl:for-each select="a">
<table>
<tr>
<td>
<xsl:value-of select="#SomeData"></xsl:value-of>
</td>
etc.
</tr>
<xsl:apply-templates select="b" mode="tmp"/>
</table>
</xsl:for-each>
</xsl:template>
<xsl:template match="node()" mode="tmp">
<tr >
<td><xsl:value-of select="#SomeOtherData"></xsl:value-of></td>
etc.
</tr>
</xsl:template>
Second xslt is similar to first one.
In Master xslt I would import both of them and in for each call templates testXslt1 and testXslt2.
This new xslt should give output one
node from first xslt, then one node
from second
Here is a simple example how this can be done:
This transformation combines the results of two templates that are applied on the XML document.
The first templates produces twice the value of every /*/* node. The second transformation produces the square of the value of every /*/* node. The results of applying the two remplates each on the XML document are mixed together in alternation as required, and this is the final result:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfResults1">
<xsl:apply-templates select="/"
mode="transform1"/>
</xsl:variable>
<xsl:variable name="vrtfResults2">
<xsl:apply-templates select="/"
mode="transform2"/>
</xsl:variable>
<xsl:variable name="vResults1"
select="ext:node-set($vrtfResults1)/node()"/>
<xsl:variable name="vResults2"
select="ext:node-set($vrtfResults2)/node()"/>
<xsl:for-each select="$vResults1">
<xsl:variable name="vPos" select="position()"/>
<xsl:copy-of select=".|$vResults2[$vPos]"/>
</xsl:for-each>
<xsl:if test=
"count($vResults2) > count($vResults2)">
<xsl:copy-of select=
"$vResults2[position()>count($vResults1)]"/>
</xsl:if>
</xsl:template>
<xsl:template match="/" mode="transform1">
<xsl:for-each select="/*/*">
<xsl:copy>
<xsl:value-of select=".+."/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
<xsl:template match="/" mode="transform2">
<xsl:for-each select="/*/*">
<xsl:copy>
<xsl:value-of select=".*."/>
</xsl:copy>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on this XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
<num>2</num>
<num>1</num>
<num>4</num>
<num>4</num>
<num>6</num>
<num>9</num>
<num>8</num>
<num>16</num>
<num>10</num>
<num>25</num>
<num>12</num>
<num>36</num>
<num>14</num>
<num>49</num>
<num>16</num>
<num>64</num>
<num>18</num>
<num>81</num>
<num>20</num>
<num>100</num>