XPath 1.0 fetch the child record - xpath

Below is the XML
<on-error-continue type="APIKIT:BAD_REQUEST" enableNotifications="true" logException="true">
<set-variable value="200" doc:name="httpStatus" variableName="httpStatus" />
<set-variable value="Bad request" doc:name="logDescription" variableName="logDescription" />
<flow-ref doc:name="global-prepare-error-response-sub-flow" name="global-prepare-error-response-sub-flow"/>
</on-error-continue>
<on-error-continue type="APIKIT:TOO_MANY_REQUEST" enableNotifications="true" logException="true">
<set-variable value="200" doc:name="httpStatus" variableName="httpStatus" />
<set-variable value="Many request" doc:name="logDescription" variableName="logDescription" />
<flow-ref doc:name="global-prepare-error-response-sub-flow" name="global-prepare-error-response-sub-flow"/>
</on-error-continue>
Wanted to get the single record
"set-variable value="200" doc:name="httpStatus" variableName="httpStatus"
using xPath 1.0 expression: Parent is -->on-error-continue type="APIKIT:BAD_REQUEST" and child is -->set-variable value = "200".
Have tried below expression. It is working fine with Xpath2.0 but not working with 1.0
//*[local-name()='on-error-continue'][#*[local-name()='type' and .='APIKIT:BAD_REQUEST']]/set-variable[#value='200' and #variableName='httpStatus']

Using this handy website, I took the xml and put it in a root element, <root>YOUR XML</root>.
With this XPath:
//root/on-error-continue[#type='APIKIT:TOO_MANY_REQUEST']/set-variable[#value='200' and #variableName='httpStatus']
I was able to extract the matching record. Try it yourself and replace the root with * in the above XPath. You should see the records that you're seeking.
The wildcard operator can be used like any element in the path.

Related

XPath 2 expressions interfering with each other

I have a XML like this:
<Values>
<Value AttributeID="asset.extension">pdf</Value>
<Value AttributeID="asset.size">10326</Value>
<Value AttributeID="ATTR_AssetPush_Webshop">1</Value>
<Value AttributeID="asset.format">PDF (Portable Document Format application)</Value>
<Value AttributeID="asset.mime-type">application/pdf</Value>
<Value AttributeID="asset.filename">filename.pdf</Value>
<Value AttributeID="asset.uploaded">2018-01-10 17:05:39</Value>
<Value AttributeID="ATTR_Verwendungsort" Derived="true">WebShop,</Value>
</Values>
I have 2 (or more) XPath-expressions like this:
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values/Value[not(#AttributeID='asset.mime-type')]" />
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values/Value[not(#AttributeID='asset.size')]" />
For some reason though, If I have 2 of them together, all information are being stripped. If I use only 1 expressoin, I get my desired output. Can't I use 2 expressions like this?
I also tried combining them like this:
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values/Value[not(#AttributeID='asset.mime-type') and (#AttributeID='asset.size')]" />
But that didn't do it, either.
The desired output would be like this:
<Values>
<Value AttributeID="asset.size">10326</Value>
<Value AttributeID="asset.mime-type">application/pdf</Value>
</Values>
I think in XSLT 2/3 you could express it as
<xsl:template match="Values/Value[not(#AttributeID = ('asset.mime-type', 'asset.size'))]"/>
In XSLT/XPath 1.0 you would need Values/Value[not(#AttributeID = 'asset.mime-type' or #AttributeID = 'asset.size')].
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values
/Value[not(#AttributeID='asset.mime-type')]" />
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values
/Value[not(#AttributeID='asset.size')]" />
This is a logical error -- has nothing to do with XPath.
It is like saying:
From all days of the week I will work only on Mondays
From the days selected above I will work only on Tuesdays
The first statement above selects only Mondays. The 2nd statement selects all Tuesdays from these Mondays -- that is the empty set.
A correct statement:
From all days of the week I will work only on Mondays or Tuesdays

How to extract a node value from an xml in Mule

This is my source xml sample.
<?xml version="1.0" encoding="UTF-8"?>
<XML>
<Meta>
<Status>Success</Status>
<Debug></Debug>
</Meta>
<Result>
<Surveys>
<element id='0'>
<responses>6</responses>
<SurveyType>SS</SurveyType>
<SurveyID>SV_01C7i5l62dnKTel</SurveyID>
<SurveyName>Georgia Dome GS</SurveyName>
<SurveyDescription>Georgia Dome</SurveyDescription>
<SurveyOwnerID>UR_8IZEh6bVlQaF41L</SurveyOwnerID>
<DivisionID>DV_2cmHYrtm8C93T6J</DivisionID>
<SurveyStatus>Active</SurveyStatus>
<SurveyStartDate>0000-00-00 00:00:00</SurveyStartDate>
<SurveyExpirationDate>0000-00-00 00:00:00</SurveyExpirationDate>
<SurveyCreationDate>2014-06-18 15:14:48</SurveyCreationDate>
<CreatorID>UR_8IZEh6bVlQaF41L</CreatorID>
<LastModified>2014-10-24 14:01:23</LastModified>
<LastActivated>2014-06-24 09:39:23</LastActivated>
<GroupName>Analytics</GroupName>
<GroupID>GR_3kMZEX6m1IqqSjP</GroupID>
<UserLastName>Parrott-Sheffer</UserLastName>
<UserFirstName>Brandon</UserFirstName>
</element>
I would like to print a list of all the value that come in the tag - <SurveyID>.
The following is my Mule flow:
</flow>
</flow>
<flow name="testFlow1" doc:name="testFlow1">
<file:inbound-endpoint path="C:\Data\Mule\deploy\out" responseTimeout="10000" doc:name="File"/>
<file:file-to-string-transformer doc:name="File to String"/>
<logger message="*********First Message - ********* #[message.payload.toString()]" level="ERROR" doc:name="Logger"/>
<foreach collection="#[xpath('//XML/Result/Surveys/element')]" doc:name="For Each">
<set-variable doc:name="Variable" value="#[xpath('SurveyID/text()').text]" variableName="id"/>
<logger level="INFO" message="********* The ID is - #[flowVars['id']]" doc:name="Logger"/>
</foreach>
</flow>
But the result I see on the console is -
INFO 2015-01-20 17:03:35,527 [[REST-API].testFlow1.stage1.02]
org.mule.transformer.simple.AddFlowVariableTransformer: Variable with
key "id", not found on message using
"#[xpath('//SurveyID/text()').text]".Since the value was marked optional,
nothing was set on the message for this variable
INFO 2015-01-2017:03:35,527 [[REST-API].testFlow1.stage1.02] org.mule.api.processor.LoggerMessageProcessor: null
I am getting this xml from a third party and I noticed it does not have any namespace information. Can you please help correct my xpath and display the values.
I am using Mule studio 3.5
First of all I need to say your XML is not valid as it doesn't have ending tags as mentioned by Victor
Anyways,if you make it correct then, You can easily get the value of all SurveyID nodes using XPATH3 and splitter
<flow name="testFlow">
<http:listener config-ref="HTTP_InboundRequest" path="/test" doc:name="HTTP"/>
<splitter expression="#[xpath3('//XML/Result/Surveys/element',message.payload,'NODESET')]" doc:name="Splitter"/>
<logger level="INFO" message="#[xpath3('SurveyID')]" doc:name="Logger"/>
</flow>
It will successfully print all the values of SurveyID
I am not familiar with Mule, only with XPath. But
SurveyID/text()
as in
<set-variable doc:name="Variable" value="#[xpath('SurveyID/text()').text]" variableName="id"/>
would be more logical in my opinion, because inside this for loop, it does not make sense to use an expression that starts with //. Let me know if it works.
You should also slightly modifiy the other XPath expression, also removing the //, then the whole should be
<foreach collection="#[xpath('/XML/Result/Surveys/element')]" doc:name="For Each">
<set-variable doc:name="Variable" value="#[xpath('SurveyID/text()').text]" variableName="id"/>
<logger level="INFO" message="#[flowVars['id']]" doc:name="Logger"/>
</foreach>
Finally, I'm not sure why you select text nodes and then write .text. To me, it seems like you are doing the same thing twice. What does
#[xpath('SurveyID').text]
do?
The xpaths you are using should work, although they could be optimized.
The sample xml looks ok too except for the fact that lacks the closing of three elements:
</Surveys>
</Result>
</XML>
My guess is that sometimes it fails for your depending on the input. I do recommend to put a
<logger message="#[message.payload]" level="ERROR" />
right before the foreach and inside the foreach.

Summary table with xForms

I have an xml like the following:
<table1>
<row>
<person>person1</person>
<value>10</value>
</row>
<row>
<person>person2</person>
<value>20</value>
</row>
<row>
<person>person1</person>
<value>5</value>
</row>
</table1>
<summaryTable>
<row>
<person>person1</person>
<value_total/>
</row>
<row>
<person>person2</person>
<value_total/>
</row>
</summaryTable>
With XForms 1 (there is no option to switch to XForms 2), using framework betterform, I want to calculate the values in the summary table, by doing the SUM of the rows in 'table1' that have the same person name. To do that I have the following binds:
<xf:bind id="bind_table1"
nodeset="table1" repeatableElement="row">
<xf:bind id="bind_head_table1" nodeset="head" />
<xf:bind id="bind_row_table1" nodeset="row">
<xf:bind id="bind_person" nodeset="person" type="xf:string" />
<xf:bind id="bind_value" nodeset="value" type="xf:integer" />
</xf:bind>
</xf:bind>
<xf:bind id="bind_summaryTable"
nodeset="summaryTable"
repeatableElement="row">
<xf:bind id="bind_head_summaryTable" nodeset="head" />
<xf:bind id="bind_row_summaryTable" nodeset="row">
<xf:bind id="bind_person_name" nodeset="person_name" type="xf:string" readonly="true"/>
<xf:bind id="bind_value_total" nodeset="value_total" type="xf:integer" readonly="true" calculate="SUM(//table1/row[person/text() = ../person_name/text()]/value)"/>
</xf:bind>
</xf:bind>
What I want to have at the end is the value_total for person1 = 15 and value_total for person2 = 20, but using this 'calculate' expression I'm getting 'NaN'. If I replace the calculate expression to compare with a literal String like:
<xf:bind id="bind_value_total" nodeset="value_total" type="xf:integer" readonly="true" calculate="SUM(//table1/row[person/text() = 'person1']/value)"/>
then I get as value_total 15 (the sum is correctly done). So it seems that the error is in the comparison expression person/text() = ../person_name/text() . Does someone have an idea about how should be the correct expression?
Thanks
Try the context() function in the calculate attribute to refer to the current node, like this:
<xf:bind nodeset="summaryTable/row/value_total" calculate="sum(//table1/row[person/text() = context()/../person/text()]/value)"/>
The context function gives you the current context node. If your bind references a nodeset with multiple nodes, it will be evaluated one time for every node, and that node is what context() returns.
It works for me with XSLTForms, maybe your version of betterForm supports it.

XPath in Nokogiri returning empty array [] whereas I am expecting to have results

I am trying to parse XML files using Nokogiri, Ruby and XPath. I usually don't encounter any problem but with the following I can't make any xpath request:
doc = Nokogiri::HTML(open("myfile.xml"))
doc.("//Meta").count
# result ==> 0
doc.xpath("//Meta")
# result ==> []
doc.xpath(.).count
# result => 1
Here is an simplified version of my XML File
<Answer xmlns="test:com.test.search" context="hf%3D10%26target%3Dst0" last="0" estimated="false" nmatches="1" nslices="0" nhits="1" start="0">
<time>
...
</time>
<promoted>
...
</promoted>
<hits>
<Hit url="http://www.test.com/" source="test" collapsed="false" preferred="false" score="1254772" sort="0" mask="272" contentFp="4294967295" did="1287" slice="1">
<groups>
...
</groups>
<metas>
<Meta name="enligne">
<MetaString name="value">
</MetaString>
</Meta>
<Meta name="language">
<MetaString name="value">
fr
</MetaString>
</Meta>
<Meta name="text">
<MetaText name="value">
<TextSeg highlighted="false" highlightClass="0">
La
</TextSeg>
</MetaText>
</Meta>
</metas>
</Hit>
</hits>
<keywords>
...
</keywords>
<groups>
...
</groups>
How can I get all children of <Hit> from this XML?
Include the namespace information when calling xpath:
doc.xpath("//x:Meta", "x" => "test:com.test.search")
You can use the remove_namespaces! method and save your day.
This is one of the most FAQ XPAth questions -- search for "XPath default namespace".
If there is no way to register a namespace for the default namespace and use the registered prefix (say "x" in //x:Meta) then use:
//*[name() = 'Meta` and namespace-uri()='test:com.test.search']
If it is known that Meta can only belong to the default namespace, then the above can be shortened to:
//*[name() = 'Meta`]

How to reference an XML attribute using XPath?

My XML:
<root>
<cars>
<makes>
<honda year="1995">
<model />
<!-- ... -->
</honda>
<honda year="2000">
<!-- ... -->
</honda>
</makes>
</cars>
</root>
I need a XPath that will get me all models for <honda> with year 1995.
so:
/root/cars/makes/honda
But how to reference an attribute?
"I need a XPath that will get me all models for <honda> with year 1995."
That would be:
/root/cars/makes/honda[#year = '1995']/model
Try /root/cars/makes/honda/#year
UPDATE: reading your question again:
/root/cars/makes/honda[#year = '1995']
Bottom line is: use # character to reference xml attributes.

Resources