I have a SharePoint list I converted to XSLT to do some additional grouping and counting and percentages. I need to return the number of items = true within my nodeset, I have:
<xsl:value-of select="count($nodeset/#PartnerArrivedAtCall)"/>
(which returns the count of all the nodes)
I have tried
<xsl:value-of select="count($nodeset/#PartnerArrivedAtCall
[#PartnerArrivedAtCall = 'Yes'])"/>
(returns zero)
and
<xsl:variable name="ArrivedYes"
select="$nodeset/#PartnerArrivedAtCall
[#PartnerArrivedAtCall='Yes']"/>
(also returns zero)
Can you please give me a good example of how to count only the true values (in my XML, true = "Yes")
Thanks!
Try
count($nodeset[#PartnerArrivedAtCall = 'Yes'])
Why don't you use out of the box ddwrt namespace functions to return list property: itemCount
http://msdn.microsoft.com/en-us/library/dd583143(v=office.11).aspx#officesharepointddwrt_listproperty
I have tried xsl:value-of
select="count($nodeset/#PartnerArrivedAtCall[#PartnerArrivedAtCall
= 'Yes')" (returns zero)
Yes, this is correct: an attribute cannot have attributes itself -- in the above you want to count PartnerArrivedAtCall attributes whose PartnerArrivedAtCall attribute has a specific value.
.
.
and xsl:variable name="ArrivedYes"
select=
"$nodeset/#PartnerArrivedAtCall[#PartnerArrivedAtCall='Yes']"
(also returns zero)
This has exactly the same problem as the previous instruction.
Solution:
Use:
$nodeset/#PartnerArrivedAtCall[. ='Yes']
Related
I have a question about the use of group-adjacent.
I have seen two patterns being used:
Pattern 1:
<xsl:for-each-group select="*" group-adjacent="boolean(self::p[#class = 'code'])">
Pattern 2:
<xsl:for-each-group select="*" group-adjacent="#class">
Based on what is used, I noticed that current-grouping-key() returns a false.
What is the purpose of using a boolean function in group-adjancent ?
With the form <xsl:for-each-group select="*" group-adjacent="boolean(self::p[#class = 'code'])"> the grouping key is a boolean value that is true for adjacent p elements having the class attribute with value code while with the second form <xsl:for-each-group select="*" group-adjacent="#class"> the grouping value is a string and groups all adjacent elements with the same class attribute values.
So it depends on your needs, if you have e.g.
<items>
<item class="c1">...</item>
<item class="c1">...</item>
<item class="c2">...</item>
</items>
you can use the second approach to group on the class value.
On the other hand, if you want to identify adjacent p elements with a certain class attribute, as e.g. in
<body>
<h1>...</h1>
<p class="code">...</p>
<p class="code">...</p>
<h2>...</h2>
<p class="code">...</p>
</body>
then the first approach allows that.
Based on what is used, I noticed that current-grouping-key() returns a
false.
current-grouping-key() returns either true or false, depending on the current group. In your first example, current-grouping-key() will be true for any group of adjacent p elements of class "code", false for the other groups.
What is the purpose of using a boolean function in group-adjancent ?
Without this, the grouping key would be the result of evaluating the expression self::p[#class = 'code'] which returns an empty sequence, which in turn causes an error.
I have the following XML snippet:
<figure customer="ABC DEF">
<image customer="ABC"/>
<image customer="XYZ"/>
</figure>
I'd like to check if the figure element's customer attribute contains the customer attributes of the image elements.
<xsl:if test="contains(#customer, image/#customer)">
...
</xsl:if>
I get an error saying:
a sequence of more than one item is not allowed as the second argument of contains
It's important to note that I cannot tell the values of the customer attributes in advance, thus using xsl:choose is not an option here.
Is it possible to solve this without using xsl:for-each?
In XSLT 2.0 you can use:
test="image/#customer/contains(../../#customer, .) = true()"
and you will get a true() result if any of them are true. Actually, that leads me to suggest:
test="some $cust in image/#customer satisfies contains(#customer, $cust)"
but that won't address the situation where the customer string is a subset of another customer string.
Therefore, perhaps this is best:
test="tokenize(#customer,'\s+') = image/#customer"
... as that will do a string-by-string comparison and give you true() if any of the tokenized values of the figure attribute is equal to one of the image attributes.
I'm a bit confused about this key function:
<xsl:for-each select="article[count(. | key('idkey', #id)[1]) = 1]>
Is there anyone that can briefly explain whats happening in this for-each loop?
The key is: <xsl:key name="idkey" match="/newspapers/newspaper" use="#id"/>
#id is an attribute in newspaper.
Thanks.
The expression key('idkey', #id)[1] selects the first element whose idkey is equal to #id.
The expression count(A|B) = 1 is an insane XSLT 1.0 workaround for testing whether A and B are the same node. (You will also see people using generate-id(A)=generate-id(B) for this.)
Put these together and you are asking whether the current element is the first one in the document that has a particular id value.
This is the basis of the technique called Muenchian Grouping (which becomes redundant in XSLT 2.0).
There is something fishy about the code because the key seems to be matching newspaper id's, not article id's. But perhaps they are related in some way.
In this for-each element
<xsl:for-each select="article[count(. | key('idkey', #id)[1]) = 1]">
The for-each is being applied to the first article element for each #id attribute.
The call key('idkey', #id) is selecting all article elements with the same #id attribute as the current one.
key('idkey', #id)[1] selects the first of all article elements with the same #id.
Because a node cannot appear in a node set more than once, the union . | key('idkey', #id)[1] will contain one node if the current article is the same node as the first article with the same #id. Otherwise it will contain two.
Checking that the value of count() is one selects only the elements that are the first with any #id.
An alternative way of doing this, and the one I prefer, is to use generate-id like this
select="article[generate-id() = generate-id(key('idkey', #id)[1])]"
which checks directly whether the current element is the same one as the first element in the set by comparing their generated IDs.
I have an XML that is converted from a java map. So all the map keys are converted into node names. The XML structure is as below
<map>
<firstName>AAA</firstName>
<firstName1>BBB</firstName1>
<firstName2>CCC</firstName2>
<firstName3>DDD</firstName3>
</map>
I am trying to write a for-each loop to extract data from this XML to create an output XML. I have tried most of the options available such as name(), local-name(), contains(), etc but couldn't come up with something that worked. What are the options available since the incremental node name can go upto count 100 or more. Any inputs in coding the loop would be of great help. I am using XSLT 1.0.
There are many ways to select the children of the top element (map):
/*/*
This selects all elements that are children of the top element of the XML document.
/*/*[starts-with(name(), 'firstName')]
This selects all top element's children-elements, whose name starts with the string 'firstName'.
/*/*[starts-with(name(), 'firstName')
and floor(substring-after(name(), 'firstName')) = substring-after(name(), 'firstName')) ]
This selects all top element's children-elements, whose name starts with the string 'firstName' and the remaining substring after this is an integer.
/*/*[starts-with(name(), 'firstName')
and translate(name(), '0123456789', '') = 'firstName')) ]
This selects all top element's children-elements, whose name starts with the string 'firstName' and the remaining substring after this contains only digits.
Finally, in XPath 2.0 (XSLT 2.0) one can use regular expressions:
/*/*[matches(name(), '^firstName\d+$')]
This will select all the first level elements and their information, which you can then use as you wish:
<xsl:for-each select="/*/*">
<xsl:value-of select="local-name()"/>
<xsl:value-of select="."/>
</xsl:for-each>
I am creating XSLT file.
I have one variable which take value from XML file.But it may happen that there is no reference in xml for the value and at that time XSL variable will return False/None(don't know).I want keep condition like,If there is no value for the variable use the default one.
How to do that ?
With the few details given in the question, the simplest test you can do is:
<xsl:if test="$var">
...
</xsl:if>
Or you might use xsl:choose if you want to provide output for the else-case:
<xsl:choose>
<xsl:when test="not($var)"> <!-- parameter has not been supplied -->
</xsl:when>
<xsl:otherwise> <!--parameter has been supplied --> </xsl:otherwise>
</xsl:choose>
The second example will also handle the case correctly that the variable or parameter has not been supplied with an actual value, i.e. it equals the empty string. This works because not('') returns true.
You haven't explained what you mean by "has no value". Here is a generic solution:
not($v) and not(string($v))
This expression evaluates to true() iff $v "has no value".
Both conditions need to be met, because a string $v defined as '0' has a value, but not($v) is true().
In XSLT 1.0 using a default can be achieved in different ways if the "value" is a node-set or if the value is a scalar (such as a string, a number or a boolean).
#Alejandro provided one way to get a default value if a variable that is supposed to contain a node-set is empty.
If the variable is supposed to contain a scalar, then the following expression returns its value (if it has a value) or (otherwise) the desired default:
concat($v, substring($default, 1 div (not($v) and not(string($v)))))
You can use string-length to check, if a variable called $reference for example contains anything.
<xsl:choose>
<xsl:when test="string-length($reference) > 0">
<xsl:value-of select="$reference" />
</xsl:when>
<xsl:otherwise>
<xsl:text>some default value</xsl:text>
</xsl:otherwise>
</xsl:choose>
If necessary use normalize-space, too.
First, all variables has values, because XSLT belongs to declarative paradigm: there is no asignation instruction, but when you declare the variable you are also declaring the expression for its value relationship.
If this value it's a node set data type (that looks from your question), then you should test for an empty node set in case nothing was selected. The efective boolean value for an empty node set is false. So, as #0xA3 has answered: test="$node-set".
You wrote:
If there is no value for the variable
use the default one. How to do that ?
Well, that depends on what kind of data type you are looking for.
Suppose the node set data type: if you want $node-set-1 value or $node-set-2 if $node-set-1 is empty, then use:
$node-set-1|$node-set-2[not($node-set-1)]
I tried a lot of solution from SO, my last solution was taken from #dimitre-novatchev, but that one also not working every time. Recently I found one more solution from random google search, thought to share with the community.
In order to check empty variable value, we can declare an empty variable and compare its value against test condition. Here is code snippet:
<xsl:variable name="empty_string"/>
<xsl:if test="testVariableValue != $empty_string">
...
</xsl:if>
Here testVariableValue hold the value of new variable to be tested for empty
scenario.
Hope it would help to test empty variable state.