Xpath conditioning in datamapper using rule - xpath

If the below XML is evaluated using datamapper to map only even numbered id's to output then the condition using xpath(Rule) is : ?
<employees>
<employee>
<id>1</id>
<name>aaa</name>
</employee>
<employee>
<id>2</id>
<name>bbb</name>
</employee>
<employee>
<id>3</id>
<name>ccc</name>
</employee>
<employee>
<id>4</id>
<name>ddd</name>
</employee>
</employees>
I tried using /employee[id mod 2 = 0]/id. It maps if the input has only one even number and fails if it has more than one even numbered id in the input.xml.
The error is
Result of xpath filling field 'id' contains two or more values!

Rules are independent to each Element Mapping configured in DM. Rules are to be created in the element mapping configuration it is intended to be used in.
For the sample xml provided, this rule should be created in Foreach 'employee' -> 'employee' element mapping.
Try this for the rule configuration
Name : {Rule Name}
Type : Boolean
Context : /employees/employee
Xpath : /matches(string(id mod 2), '0')
This creates a rule returning true if id is even and false if id is odd.
Use an if block in DM script view and check for the rule's value. if rule value is true, only then map the id.

It also worked in this way
Name : {Rule Name}
Type : String
Context : /employees/employee
Xpath : /id mod 2 = 0
and used the if condition as below :
if(input.Ruleid.equals("true")){
output.id = input.id;
output.name = input.name;
}
Is there any other way to do this without using if condition.

Related

Select Xpath element value based on value of another element

I am trying to capture a value using XPath based on value of a different field.
Example XML:
<?xml version="1.0" encoding="UTF-8" ?>
<employees>
<employee>
<id>1</id>
<firstName>Tom</firstName>
<lastName>Cruise</lastName>
<photo>https://jsonformatter.org/img/tom-cruise.jpg</photo>
</employee>
<employee>
<id>2</id>
<firstName>Maria</firstName>
<lastName>Sharapova</lastName>
<photo>https://jsonformatter.org/img/Maria-Sharapova.jpg</photo>
</employee>
<employee>
<id>3</id>
<firstName>Robert</firstName>
<lastName>Downey Jr.</lastName>
<photo>https://jsonformatter.org/img/Robert-Downey-Jr.jpg</photo>
</employee>
</employees>
I am trying to get Xpath expression for value in the firstName field, when id value is 3.
You can locate parent node based on the known child node and then find the desired child node of that parent, as following:
//employee[./id='3']/firstName
the expression above will give the desired firstName node itself.
To retrieve it's text value this can be used:
//employee[./id='3']/firstName/text()

SchemaTron rule to find invalid records

I am trying to validate the following XML using the Schematron rule.
XML:
<?xml version="1.0" encoding="utf-8"?>
<Biotic><Maul><Number>1</Number>
<Record><Code IDREF="a1"/>
<Detail><ItemID>1</ItemID></Detail>
<Detail><ItemID>3</ItemID></Detail>
</Record>
<Record><Code IDREF="b1"/>
<Detail><ItemID>3</ItemID></Detail>
<Detail><ItemID>4</ItemID></Detail>
</Record>
<Record><Code IDREF="b1"/>
<Detail><ItemID>4</ItemID></Detail>
<Detail><ItemID>6</ItemID></Detail>
</Record>
<Record><Code IDREF="c1"/>
<Detail><ItemID>5</ItemID></Detail>
<Detail><ItemID>5</ItemID></Detail>
</Record>
</Maul></Biotic>
And the check is "ItemID should be unique for the given Code within the given Maul."
So as per requirement Records with Code b1 is not valid because ItemId 4 exists in both records.
Similarly, record C1 is also not valid because c1 have two nodes with itemId 5.
Record a1 is valid, even ItemID 3 exists in the next record but the code is different.
Schematron rule I tried:
<?xml version="1.0" encoding="utf-8" ?><schema xmlns="http://purl.oclc.org/dsdl/schematron" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<title>Schematron validation rule</title>
<pattern id="P1">
<rule context="Maul/Record" id="R1">
<let name="a" value="//Detail/[./ItemID, ../Code/#IDREF]"/>
<let name="b" value="current()/Detail/[./ItemID, ../Code/#IDREF]"/>
<assert test="count($a[. = $b]) = count($b)">
ItemID should be unique for the given Code within the given Maul.
</assert>
</rule>
</pattern>
</schema>
The two let values seem problematic. They will each return a Detail element (and all of its content including attributes, child elements, and text nodes). I'm not sure what the code inside the predicates [./ItemID, ../Code/#IDREF] is going to, but I think it will return all Detail elements that have either a child ItemID element or a sibling Code element with an #IDREF attribute, regardless of what the values of ItemID or #IDREF are.
I think I would change the rule/#context to ItemID, so the assert would fail once for each ItemID that violates the constraint.
Here are a rule and assert that work correctly:
<?xml version="1.0" encoding="utf-8" ?><schema xmlns="http://purl.oclc.org/dsdl/schematron" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<title>Schematron validation rule</title>
<pattern id="P1">
<rule context="Maul/Record/Detail/ItemID" id="R1">
<assert test="count(ancestor::Maul/Record[Code/#IDREF = current()/ancestor::Record/Code/#IDREF]/Detail/ItemID[. = current()]) = 1">
ItemID should be unique for the given Code within the given Maul.
</assert>
</rule>
</pattern>
</schema>
The assert test finds, within the ancestor Maul, any Record that has a Code/#IDREF that equals the Code/#IDREF of the Record that the current ItemID is in. At minimum, it will find one Record (the one that the current ItemID is in). Then it looks for any Detail/ItemID within those Records that is equal to the current ItemID. It will find at least one (the current ItemID). The count function counts how many ItemIDs are found. If more than one is found, the assert fails.
Thanks for the reference to https://www.liquid-technologies.com/online-schematron-validator! I wasn't aware of that tool.

JMeter Xpath2 extractor id's matching multiple conditions

Below is the sample XML fragment, from it i'm trying to filter out id's of articles matching both conditions as below. Currently i could extract id's for individual condition with help of expression below
get Avaialable articles, Xpath2 expression = (//*//*//*//*[starts-with(state,'Avaialable')])/id
get articles name starting with 'A' () , Xpath2 expression = (//*//*//*//*[starts-with(name,'A')])/id
I want to merge these conditions in a single expression and would like to
fetch id's of Articles where Name starts with 'A' AND articles which
are Available
. Tried multiple ways but not working as expected.
Dummy XML fragment:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns3:GetArtclesResponse
xmlns:ns2="XXX"
xmlns:ns4="XXX"
xmlns:ns3="XXX"
xmlns:ns6="XXX"
xmlns:ns5="XXX"
xmlns:ns8="XXX"
xmlns:ns7="XXX"
xmlns:ns13="XXX"
xmlns:ns9="XXX"
xmlns:ns12="XXX"
xmlns:ns11="XXX">
<serverTimeInfo timezone="+00:00" timestamp="1606409365419"/>
<items>
<count>2</count>
<articles>
<article>
<name>ABC</name>
<id>1234</id>
<state>Avaialable</state>
</article>
<article>
<name>XYZ</name>
<id>3456</id>
<state>Avaialable_Conditional</state>
</article>
</articles>
</items>
</ns3:GetArtclesResponse>
You can use and to check both:
(//*//*//*//*[starts-with(state,'Avaialable') and starts-with(name,'A')])/id
If you want to combine 2 different XPath expressions you can use | (union) operator like:
//article[state = 'Avaialable'] | //article[starts-with(name,'A')]
it will return you both:
nodes which have state=Available
and nodes which name starts with A
If you want to combine 2 conditions in a single XPath expression - go for and operator like:
//article[state = 'Avaialable' and starts-with(name,'A')]
it will return
nodes which nave state=available and whose name attribute starts with a
More information:
XPath Operators
The JMeter XPath2 Extractor: How to Achieve Better Correlations

Selecting a XML node with LINQ, and modifying

I've got the following XML:
<Config>
<Book>
<Name> Book Name #1 </Name>
<Available In>
<Country>US</Country>
<Country>Canada</Country>
</Available In>
</Book>
</Config>
I need to find all instances of Book which are available in a specific country, and then introduce a node underneath "Available In". My selection statement fails anytime I add the where statement:
XElement xmlFile = XElement.Load(xmlFileLocation);
var q = (from c in xmlFile.Elements(“Book”)
where c.Elements(Country).Value == "Canada"
select c;
.Value can't be resolved, and toString give me the entire subnode in stringform. I need to select all books in a particular country so that I can then update them all to include a new locale node, ex:
<Config>
<Book>
<Name> Book Name #1 </Name>
<Available In>
<Country>US</Country>
<Country>Canada</Country>
</Available In>
<LocaleIDs>
<LocalID> 3066 </LocaleID>
<LocaleIDs>
</Book>
</Config>
Thanks for your help!
You're trying to use Value on the result of calling Elements which returns a sequence of elements. That's not going to work - it doesn't make any sense. You want to call it on a single element at a time.
Additionally, you're trying to look for direct children of Book, which ignores the Available In element, which isn't even a valid element name...
I suspect you want something like:
var query = xmlFile.Elements("Book")
.Where(x => x.Descendants("Country")
.Any(x => (string) x == "Canada"));
In other words, find Book elements where any of the descendant Country elements has a text value of "Canada".
You'll still need to fix your XML to use valid element names though...

In XPATH, use data from the current context to filter other data

Is there a way to use data from the current context to filter nodes somewhere else in the statsource.
For instance, if I have this XML:
<root>
<group1>
<inst>
<type>Foo</type>
<value>First Foo</value>
</inst>
<inst>
<type>Bar</type>
<value>The Bar</value>
</inst>
<inst>
<type>Foo</type>
<value>Second Foo</value>
</inst>
</group1>
<group2>
<Filter>
<FilterType>Foo</FilterType>
</Filter>
<Filter>
<FilterType>Bar</FilterType>
</Filter>
</group2>
</root>
Assuming my context is one of the Filter tags, I want to return get the number of instances of the specified type in group1. I would like to write XPATH that looks something like this:
count(/root/group1/inst[type = **FilterType**])
Is there anything I can use to get the FilterType in the original context?
This can be done easily in XPath 2.0:
for $type in /*/*/Filter[1]/FilterType
return
count(/*/group1/*[type eq $type])
When this Xpath expression is evaluated against the provided XML document, the correct result is returned:
2
In XPath 1.0, if the number of group1/inst elements is known in advance, and $vType stands for the FilterType in question, then one could construct the following XPath 1.0 expression:
($vType = /*/group1/inst[1]/type)
+
($vType = /*/group1/inst[2]/type)
+
($vType = /*/group1/inst[3]/type)
which again produces:
2.
Finally, if the XPath 1.0 expression is needed in XSLT and the "Filter" is the current node,
then the following XPath expression calculates the exact number of matches:
count(/*/group1/inst[type = curent()/FilterType])
I'm looking for a solution to the same problem. Currently, I'm using a variable to route around this... it'd look something like the following
<xsl:variable name='FilterType'><xsl:value-of select='FilterType'/></xsl:variable>
<xsl:value-of select='count(/root/group1/inst[type = $FilterType])'/>
But there has to be a better way.

Resources