Reputation: 125
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?
Upvotes: 1
Views: 124
Reputation: 1882
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]
Upvotes: 2
Reputation: 243499
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>
Upvotes: 1