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())])
Related
how to use pattern and group for validating property.
<property name="string" [action=set|remove] [type="string"]
(value="literal" | expression="xpath")
[scope=default|transport|axis2|axis2-client] [pattern="regex"
[group="integer"]]>
</property>
Pattern in Property mediator can be used to match the value or the result of an expression against a given pattern. If the pattern matches, Property mediator returns the value. Else, it returns an empty string.
For example, in the following example, only Test1 property value matches with the pattern. Hence, only Test1 returns the value. Test2 returns an empty string.
<property name="Test1" value="5" scope="default" type="STRING" pattern="[0-9]" group="0"/>
<property name="Test2" value="20" scope="default" type="STRING" pattern="[0-9]" group="0"/>
<log level="custom">
<property name="Test1 Value : " expression="get-property('Test1')"/>
<property name="Test2 Value : " expression="get-property('Test2')"/>
</log>
And, "group" attribute in Property mediator is intended to be used to evaluate against the number of capturing groups in this matcher's pattern, similar to java.util.regex.Matcher.groupCount() in Java. However, apparently the group support is not available in WSO2 EI 6.5.0 at the moment.
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.
I have my request body as:
<tns:InputRequest xmlns:tns="http://tempuri.org/">
<tns:ID>ID_001</tns:ID>
<tns:ID>ID_002</tns:ID>
<tns:Description>Description for ID_001</tns:Description>
<tns:Description>Description for ID_002</tns:Description>
</tns:InputRequest>
and to get the value of ID and Description, i Have created property as:
<property xmlns:tns="http://tempuri.org/" name="ID" expression="//tns:ID" scope="default" type="STRING"/>
<property xmlns:tns="http://tempuri.org/" name="Description" expression="//tns:Description" scope="default" type="STRING"/>
But this gets me only one value. How can i make a property array so that i can store multiple values of ID and description in it and how to retreive from this array property?Looking forward to your reply.Thanks in advance
You should be able to extract those values using XPATH (//node/child::node()) and then set to property.
Below thread will help you to extract required nodes and set to property. You need to set the type as 'OM' to preserve XML as it is.
how to catch an array of nodes to a property
<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.
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