How to extract a node value from an xml in Mule - xpath

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.

Related

XPath 1.0 fetch the child record

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.

Sort multiple XML by element value from another XML

Basically what I'm trying to here is to merge and sort a multiple XML by a value of an element in a reference XML using XSLT.
> <xsl:variable name="refXml"
> select="document(concat(replace($refXmlTemp,'^file:',''),'/ref.xml'))"/>
>
>
> <xsl:for-each select="for $x in
> collection(string-join(($inputDir,'select=*.xml;recurse=yes;on-error=fail'),'?'))
> return
> (if (matches($refXml/root/descendant-or-self::issue/id[normalize-space(.)=normalize-space($x/art/item/id)]/number,'\w+')
> and matches($x/art/item/title,'\w+')) then saxon:discard-document($x)
> else ())">
> <xsl:sort select="$refXml/root/descendant-or-self::issue/id[normalize-space(.)=/art/item/id]/following-sibling::number"/>
The snippet above merged all the input XML but it was not sorted.
It seems that the XSLT xsl:sortfunction will only take an effect if it will be pointed at a value inside the XML that's currently processing.
Please advise on how could i get to use the ref.xml as a reference in sorting.
Here's a sample input of ref.xml:
<root>
<issue>
<id>wlu-101</id>
<number>1</number>
</issue>
<issue>
<id>wlu-143</id>
<number>2</number>
</issue>
<issue-group>
<issue>
<id>wlu-144</id>
<number>3</number>
</issue>
<issue-group>
<issue>
<id>wlu-185</id>
<number>4</number>
</issue>
</issue-group>
</issue-group>
</root>
Replace <xsl:sort select="$refXml/root/descendant-or-self::issue/id[normalize-space(.)=/art/item/id]/following-sibling::number"/> with
<xsl:sort select="key('ref', /art/item/id, $refXml)/number"/>
after defining
<xsl:key name="ref" match="issue" use="normalize-space(id)"/>
As an alternative use <xsl:sort select="$refXml//issue[normalize-space(id)=current()/art/item/id]/number"/>.

XPath results based on two nodes

I have XML that has a lot of duplicated values. I'd like to select all the rows with a specific section ("sec") and section tag ("sec_tag"), but I can't seem to get the XPath correct.
Here's a small snippet of the XML:
<root>
<record>
<sec>5</sec>
<sec_tag>919</sec_tag>
<nested_tag>
<info>Info</info>
<types>
<type>1</type>
<type>2</type>
<type>3</type>
</types>
</nested_tag>
<flags>00000000</flags>
</record>
<record>
<sec>5</sec>
<sec_tag>930</sec_tag>
<nested_tag>
<info>Info</info>
<types>
<type>1</type>
<type>2</type>
<type>3</type>
</types>
</nested_tag>
<flags>00000000</flags>
</record>
<record>
<sec>7</sec>
<sec_tag>919</sec_tag>
<nested_tag>
<info>Info</info>
<types>
<type>1</type>
<type>2</type>
<type>3</type>
</types>
</nested_tag>
<flags>00000000</flags>
</record>
</root>
I want the node that has <sec>5</sec> and <sec_tag>919</sec_tag>.
I tried something like this:
//sec[text(), "5"] and //sec_tag[text(), "919"]
Obviously that's not the correct syntax there, I just need to find the correct XPath expression.
You can use the following XPath expression to return record elements having child sec equals 5 and sec_tag equals 919 :
//record[sec = 5 and sec_tag = 919]

xpath expression to compare and evaluate value based on condition

<School>
<Child_One>
<Subject>
<name>computers</name>
<marks>55</marks>
<name>mathematics</name>
<marks>44</marks>
</Subject>
<Child_One>
<Child_Two>
<name>computers</name>
<marks>66</marks>
<name>mathematics</name>
<marks>77</marks>
</Child_Two>
</School>
Can anybody help me to find the Child_One subject name, in which he got highest marks
Thanks
First of all a few formatting things:
Your XML is not quite well formatted. It should have the same start and end tags
I believe the Subject element should look different then posted
When posting a input XML, don't use backticks, but indent the XML with 4 spaces to format it well on Stackoverflow
I used and changed the input XML to this:
<?xml version="1.0" encoding="UTF-8"?>
<School>
<Child_One>
<Subject>
<name>computers</name>
<marks>55</marks>
</Subject>
<Subject>
<name>mathematics</name>
<marks>44</marks>
</Subject>
</Child_One>
<Child_Two>
<Subject>
<name>computers</name>
<marks>66</marks>
</Subject>
<Subject>
<name>mathematics</name>
<marks>77</marks>
</Subject>
</Child_Two>
</School>
With XPath 2.0 you can use the following the find the max value:
/School/Child_One/Subject[marks = max(/School/Child_One/Subject/marks)]/name
With XPath 1.0 you can use the following (replace < with > to find minimum):
/School/Child_One/Subject[not(marks < /School/Child_One/Subject/marks)][1]/name

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