XSLT counting elements with a given value - xslt

I need to count the number of elements in an XML file that have a particular value (to verify uniqueness). The XML file looks like this:
EDIT: I updated the original "simplified" XML with the actual hairy mess someone designed. Unfortunately, this is going to make all the previous Answers really confusing and wrong unless edited.
<root>
<ac>
<Properties>
<Property Name="Alive">
<Properties>
<Property Name="ID">
<Properties>
<Property Name="Value">
<long>11007</long>
</Property>
</Properties>
</Property>
</Properties>
</Property>
<Property Name="Dead">
<Properties>
<Property Name="ID">
<Properties>
<Property Name="Value">
<long>11008</long>
</Property>
</Properties>
</Property>
</Properties>
</Property>
...
<Property Name="MostlyDeadAllDay">
<Properties>
<Property Name="ID">
<Properties>
<Property Name="Value">
<long>99001</long>
</Property>
</Properties>
</Property>
</Properties>
</Property>
</Properties>
</ac>
</root>
I am trying to define a variable to see how many of the properties at the Alive/Dead level have the long value (ID) as defined in a template parameter. Something along these lines (though I suspect this is wrong)...
<xsl:param name="parPropId"/>
<xsl:variable name="countProperties">
<xsl:value-of select="count(/root/ac/
Properties/Property/
Properties/Property[#Name = 'ID']/
Properites/Property[#Name = 'Value']/long = $parPropId)"/>
</xsl:variable>
There can be multiple Property elements defined at the "ID" level. But I am fairly certain I can count on just one Property element ("Value") under "ID", and only one "long" element under "Value".
[disclaimer] Whoever designed the overall XML file I'm stuck working with REALLY didn't know how to structure XML (e.g., backwards use of attributes versus elements). I fear my XSLT thinking has temporarily been warped :) as a result. [/disclaimer]

This XPath:
count(//Property[long = '11007'])
returns the same value as:
count(//Property/long[text() = '11007'])
...except that the first counts Property nodes that match the criterion and the second counts long child nodes that match the criterion.
As per your comment and reading your question a couple of times, I believe that you want to find uniqueness based on a combination of criteria. Therefore, in actuality, I think you are actually checking multiple conditions. The following would work as well:
count(//Property[#Name = 'Alive'][long = '11007'])
because it means the same thing as:
count(//Property[#Name = 'Alive' and long = '11007'])
Of course, you would substitute the values for parameters in your template. The above code only illustrates the point.
EDIT (after question edit)
You were quite right about the XML being horrible. In fact, this is a downright CodingHorror candidate! I had to keep recounting to keep track of the "Property" node I was on presently. I feel your pain!
Here you go:
count(/root/ac/Properties/Property[Properties/Property/Properties/Property/long = $parPropId])
Note that I have removed all the other checks (for ID and Value). They appear not to be required since you are able to arrive at the relevant node using the hierarchy in the XML. Also, you already mentioned that the check for uniqueness is based only on the contents of the long element.

Your xpath is just a little off:
count(//Property/long[text()=$parPropId])
Edit: Cerebrus quite rightly points out that the code in your OP (using the implicit value of a node) is absolutely fine for your purposes. In fact, since it's quite likely you want to work with the "Property" node rather than the "long" node, it's probably superior to ask for //Property[long=$parPropId] than the text() xpath, though you could make a case for the latter on readability grounds.
What can I say, I'm a bit tired today :)

<xsl:variable name="count" select="count(/Property/long = $parPropId)"/>
Un-tested but I think that should work. I'm assuming the Property nodes are direct children of the root node and therefor taking out your descendant selector for peformance

Related

Make checkstyle require variable prefix

I would like all checkbox variables to begin with "ckx".
^.*JCheckBox((?! ckx).)*$
This works for variable declarations but matches definitions too.
E.g.
ckxSomeCheckbox = new JCheckBox();
Any idea how to prevent this code to be displayed as an error?
Valid examples:
private JCheckBox ckxTest = new JCheckBox();
public javax.swing.JCheckBox ckxTest;
Invalid examples:
private JCheckBox abcTest;
private JCheckBox abcckxTest;
ckxTest = new JCheckBox();
ckxTest = new JCheckBox("");
I would recommend using the RegexpMultiline check so that you catch cases where people insert line breaks into the code in inconvenient places.
The regex would be \bJCheckBox\b\s*(?!ckx[A-Z])\w+ (explanation / examples).
<module name="RegexpMultiline">
<property name="format" value="\bJCheckBox\b\s*(?!ckx[A-Z])\w+"/>
<property name="message" value="Missing prefix 'ckx' on checkbox variable"/>
<property name="fileExtensions" value="java"/>
</module>
Note that I took out the lines where the faulty identifier is at the start of the line, because those lines would not compile. Unless you have the variable declared earlier, in which case that would be where the issue was flagged.
ckxTest = new JCheckBox();
ckxTest = new JCheckBox("");
Checking this with a regex isn't perfect, because people can use comments or subclasses to confuse the detector. So it would be a more powerful approach to use a PMD XPath rule or something, but that's for another question. :-)
The same could be achieved using RegexpSinglelineJava, which adds the ability to ignore comments, but would be derailed by unexpected line breaks:
<module name="RegexpSinglelineJava">
<property name="format" value="\bJCheckBox\b\s*(?!ckx[A-Z])\w+"/>
<property name="message" value="Missing prefix 'ckx' on checkbox variable"/>
<property name="ignoreComments" value="true"/>
</module>

XSLT: Xpath to count items with attribute value matching another specific sibling

I have the following (simplified; there are more Property-Lines) XML-File that I need to process by XSLT:
<Collection Name="Columns" Type="OutputColumn">
<SubRecord>
<Property Name="Name">SID</Property>
<Property Name="SqlType">BigInt</Property>
<Property Name="Derivation">inputstream1.SID</Property>
<Property Name="Description">Main key</Property>
</SubRecord>
<SubRecord>
<Property Name="Name">name</Property>
<Property Name="SqlType">Char</Property>
<Property Name="Derivation">inputstream2.name</Property>
<Property Name="Description">Surname</Property>
</SubRecord>
<SubRecord>
<Property Name="Name">street</Property>
<Property Name="SqlType">Char</Property>
<Property Name="Derivation">inputstream1.streetname + inputstream1.number</Property>
<Property Name="Description">Full street</Property>
</SubRecord></Collection>
I now need to know two things as a variable in XSLT to decide what to do further in an if-Condition:
Is there at least one SubRecord where substring-after(Derivation, '.') matches the sibling Property "Name" (so like in the first two SubRecords)?
Is there at least one SubRecord where substring-after(Derivation, '.') does not match the sibling Property "Name" (so like in the third SubRecord)?
I tried to do this by Xpath with the Count()-Functionality but simply can't figure out how to do the comparison to the sibling Property node with "Name" as it's name...I'm also open for other ideas if you know an alternative to Count().
I'm not sure I completely understood the issue, so check below and let me know if it's not what you actually want
First can be done with below XPath:
boolean(//SubRecord[substring-after(./Property[#Name="Derivation"]/text(), '.')=./Property[#Name="Name"]/text()])
This should return true because XPath matches first two SubRecord elements.
Using not() should allow to return true because XPath matches third SubRecord element:
boolean(//SubRecord[not(substring-after(./Property[#Name="Derivation"]/text(), '.')=./Property[#Name="Name"]/text())])

Event builder XML mapping in WSO2CEP 3.0.0

Lets say, a stream named inputStream is defined with attributes name:string, surname:string, address:string. For this stream, if an event builder is defined like the following,
<property>
<from xpath="xpathForSurname"/>
<to default="NULL" name="surname" type="string"/>
</property>
<property>
<from xpath="xpathForName"/>
<to default="NULL" name="name" type="string"/>
</property>
<property>
<from xpath="xpathForAddress"/>
<to default="NULL" name="address" type="string"/>
</property>
When I send an input like ('John', 'Lennon', 'Liverpool') I expect inputStream to be ['John', 'Lennon', 'Liverpool'], but the result stream is ['Lennon', 'John', 'Liverpool']. The reason is that values of the attributes are added to stream following the mapping sequence in builder definition.
Therefore, <to> tags in definition becomes pointless (the value upon xpathForSurname evaluation is not mapped to surname but name). Is this a bug or is it done on purpose?
Yes, this seems to be a bug in CEP 3.0.0 and will be fixed in a future release. I have created a JIRA with the information you provided in CEP-640.
For now, the workaround would be to let the input stream come directly as it is via the event builder without reordering the attributes and doing any manipulations to the ordering at the level of execution plans. Hope this workaround will work for you.

Is it possible to map External Content Type to Office Item Type without using SharePoint Designer?

I need to connect Outlook to the External list and have to map its content type to the Office Item Type for that, but unfortunately I can't see it in SPD2013. Possibly it is because BDC model uses OData, which has limited support in SPD2013 (my question about it).
So, is it possible to map External Content Type to Office Item Type without using SharePoint Designer? If so, how can I do it?
Thank you in advance.
PS: SharePoint 2013 RTM.
Hurray!
After three days of unsuccessful googling and asking this question everywhere :-) I tried to investigate it myself and at last, I have found a way to do it!
It appeared that it is quite easy to map it by hand. For that you have to make 2 changes in the BDC model file (xml with a .bdcm extension):
Add property with the name of Office item type to the entity
<Entities>
<Entity Name="AbsenceCalendar" DefaultDisplayName="AbsenceCalendar" Namespace="MyNameSpace" Version="1.0.0.0" EstimatedInstanceCount="2000">
<Properties>
<Property Name="OutlookItemType" Type="System.String">Appointment</Property>
...
</Properties>
...
Map necessary fields to the Office Item Type. For instance, for mandatory fields of Appointment Office Item Type it could look like
<Method Name="ReadSpecificAbsenceCalendarEntity" DefaultDisplayName="Read Specific AbsenceCalendarEntity" IsStatic="false">
......
<Parameter Name="#AbsenceCalendarEntity" Direction="Return">
<TypeDescriptor Name="AbsenceCalendarEntity" DefaultDisplayName="AbsenceCalendarEntity" TypeName="Microsoft.BusinessData.Runtime.DynamicType">
<TypeDescriptors>
<TypeDescriptor Name="Title" DefaultDisplayName="Title" TypeName="System.String">
<Properties>
<Property Name="OfficeProperty" Type="System.String">Subject</Property>
</Properties>
</TypeDescriptor>
<TypeDescriptor Name="StartDate" DefaultDisplayName="StartDate" TypeName="System.DateTime">
<Properties>
<Property Name="OfficeProperty" Type="System.String">Start</Property>
</Properties>
</TypeDescriptor>
<TypeDescriptor Name="EndDate" DefaultDisplayName="EndDate" TypeName="System.DateTime">
<Properties>
<Property Name="OfficeProperty" Type="System.String">End</Property>
</Properties>
</TypeDescriptor>
.......

XSLT - Accessing Key's by index - For example, in Muenchian Grouping

<listings>
<property rln="r317080" firm="f102" agent="a2140">
<street>2638 Maple Avenue</street>
<city>Padua</city>
<state>WI</state>
<zip>53701</zip>
<price>229000</price>
<style>2 Story Contemporary, Transitional</style>
<sqfeet>2328</sqfeet>
<bathrooms>2 1/2</bathrooms>
<bedrooms>4</bedrooms>
<garage>2 car, attached</garage>
<age>22</age>
<description>Very nice home on a one block dead end street with woods nearby.
Very special location for quiet and privacy! Home features open floor plan with
large rooms - new patio doors to pretty yard. updates: shingles, vinyl siding,
refrig and dishwasher, garage door. Fireplace in family room flanked by great
built-ins. add first floor laundry and award winning Padua schools.
</description>
</property>
<property ...>
<city>Broxton</city>
...
</property>
<property ...>
<city>Cutler</city>
...
</property>
<property ...>
<city>Argyle</city>
...
</property>
<property ...>
<city>Stratmore</city>
...
</property>
<property ...>
<city>Padua</city>
...
</property>
<property ...>
<city>Oseola</city>
...
</property>
<property ...>
<city>Fenmore</city>
...
</property>
<property ...>
<city>Cutler</city>
...
</property>
<property ...>
<city>Padua</city>
...
</property>
<property ...>
<city>Cutler</city>
...
</property>
<property ...>
<city>Oseola</city>
...
</property>
</listings>
In my textbook (XML 2nd Edition by Patrick Carey) it provides an example of using 'Muenchian Grouping' to find unique selections. The part I don't understand is thus:
It gets to here, in the progression of the example where it states: "
property[generate-id()=generate-id(key("cityNames", "Cutler")[1])]
" which says that this will find the first 'Cutler' in the selection, due to the index of '[1]'. Which given the XML above will return "Cutler"
Now the example progresses to thus: "
property[generate-id()=generate-id(key("cityNames", city)[1])]
" which says that this will find the first and only the first (therefore unique) of each city within the key. Creating a group of unique values of all the city's within. Which given the XML above will return "Argyle Broxton Cutler Fenmore Padua Stratmore Oseola" (note that there is no multiples).
Now, my question is thus: why does the second statement return a range of values, instead of just one?
Thanks
When you define your key, the match expression can match multiple nodes. That node-set is returned when accessing the key by name.
Adding the predicate filter for the first one ensures that you will only get at most one(the first) node returned from the key.
Ok, I suppose the answer I was looking for is thus:
property[generate-id()=generate-id(key("cityNames", city)[1])]
This code finds the first of each city
property[generate-id()=generate-id(key("cityNames", city[1]))]
and this code finds the first of all city's
easy enough, just couldn't see it before.