I need to filter tag value from the following sample XML.
<ClinicalDocument xmlns="urn:hl7-org:v3">
<id root="3930E379-5C54-477D-8DB2-F6C92BC08C691" />
<component>
<structuredBody>
<component>
<section>
<templateId root="1.3.6.1.4.1.19376.1.5.3.1.3.4"/>
<code code="10164-2" codeSystem="2.16.840.1.113883.6.1"
codeSystemName="LOINC" displayName="HISTORY OF PRESENT ILLNESS"/>
<title>HISTORY OF PRESENT ILLNESS</title>
<text>Patient slipped and fell on ice, twisting her ankle as she fell.
</text>
</section>
</component>
<component>
<section>
<templateId root="1.3.6.1.4.1.19376.1.5.3.1.3.5"/>
<code code="10164-3" codeSystem="2.16.840.1.113883.6.12"
codeSystemName="LOINC1" displayName="DEMO"/>
<title>DEMO HISTORY OF PRESENT ILLNESS</title>
<text>DEMO Patient slipped and fell on ice, twisting her ankle as she fell.
</text>
</section>
</component>
</structuredBody>
</component>
</ClinicalDocument>
there are many file like this in my collection(i am using eXits-db), and i need to filter based on 'root' attribute in <id> tag and 'root' attribute in <templateId> tag. and the result i need is only the <title> text value.
Following is the query i tried.But is shows all the title values(not the one which match my condition).
xquery version "3.0";
declare namespace d = "urn:hl7-org:v3";
(
for $prod in collection("/db/netspectivedb/")/d:ClinicalDocument
where $prod/d:id/#root/string()='3930E379-5C54-477D-8DB2-F6C92BC08C691'
and $prod/d:component/d:structuredBody/d:component/d:section/d:templateId/#root/string()='1.3.6.1.4.1.19376.1.5.3.1.3.4'
return $prod/d:component/d:structuredBody/d:component/d:section/d:title/text()
)
The problem was, that $prod in your XQuery references ClinicalDocument, which isn't specific enough for your purpose. You want to loop through component or section inside structuredBody instead to start with, for example :
declare namespace d = "urn:hl7-org:v3";
(
for $section in collection("/db/netspectivedb/")/d:ClinicalDocument[d:id/#root eq '3930E379-5C54-477D-8DB2-F6C92BC08C691']/d:component/d:structuredBody/d:component/d:section
where $section/d:templateId/#root eq '1.3.6.1.4.1.19376.1.5.3.1.3.4'
return $section/d:title/text()
)
or using nested for as you specifically asked. Nested for also turns out to be more readable in this case :
declare namespace d = "urn:hl7-org:v3";
(
for $prod in collection("/db/netspectivedb/")/d:ClinicalDocument
for $section in $prod/d:component/d:structuredBody/d:component/d:section
where $prod/d:id/#root eq '3930E379-5C54-477D-8DB2-F6C92BC08C691'
and $section/d:templateId/#root eq '1.3.6.1.4.1.19376.1.5.3.1.3.4'
return $section/d:title/text()
)
I am using eq instead of = above since we mean to do value comparison (read more: https://developer.marklogic.com/blog/comparison-operators-whats-the-difference)
You could achieve the same thing with a single XPath expression:
declare namespace d = "urn:hl7-org:v3";
collection("/db/netspectivedb/")/
d:ClinicalDocument[d:id/#root eq '3930E379-5C54-477D-8DB2-F6C92BC08C691']/
d:component/d:structuredBody/d:component/
d:section[d:templateId/#root eq '1.3.6.1.4.1.19376.1.5.3.1.3.4']/d:title/text()
Related
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
I’ve got the following XML of associated elements:
<doc>
<!-- A block of style elements. -->
<styles>
<style id='style-1' class='bar'>…</style>
<style id='style-2' class='baz'>…</style>
…
</styles>
<!-- Document content. -->
<p style='style-1'>…</p>
<p style='style-2'>…</p>
…
</doc>
For an XSLT template I’m looking for an XPath expression matches “an element p whose style is of class bar”?
Pure XPath 1.0 expression that will return all elements p whose style is of class bar :
//p[#style = //style[#class='bar']/#id]
Basically, the XPath looks for <p> elements where style attribute equals id of <style class='bar'>.
Presuming that is an accurate representation of your document's structure, I would advise using this, without double-slashes (//) since double-slashes can be very inefficient:
/doc/p[#style = /doc/styles/style[#class = 'bar']/#id]
I have posted sample XML and expected output kindly help to get the result.
Sample XML
<root>
<A id="1">
<B id="2"/>
<C id="2"/>
</A>
</root>
Expected output:
<A id="1"/>
You can formulate this query in several ways:
Find elements that have a matching attribute, only ascending all the time:
//*[#id=1]
Find the attribute, then ascend a step:
//#id[.=1]/..
Use the fn:id($id) function, given the document is validated and the ID-attribute is defined as such:
/id('1')
I think it's not possible what you're after. There's no way of selecting a node without its children using XPATH (meaning that it'd always return the nodes B and C in your case)
You could achieve this using XQuery, I'm not sure if this is what you want but here's an example where you create a new node based on an existing node that's stored in the $doc variable.
declare variable $doc := <root><A id="1"><B id="2"/><C id="2"/></A></root>;
element {fn:node-name($doc/*)} {$doc/*/#*}
The above returns <A id="1"></A>.
is that what you are looking for?
//*[#id='1']/parent::* , similar to //*[#id='1']/../
if you want to verify that parent is root :
//*[#id='1']/parent::root
https://en.wikipedia.org/wiki/XPath
if you need not just parent - but previous element with some attribute: Read about Axis specifiers and use Axis "ancestor::" =)
<results>
{
for $p in
(
for $o in doc("mondial-3.0.xml") /mondial/organization
where fn:count($o/members)
order by fn:count($o/members) descending
return <organization>
<name> {$o/#name/string()} </name>
<abbreviation> {$o/#abbrev/string()} </abbreviation>
<num_members> {fn:count($o/members)} </num_members>
<members> {for $m in doc("mondial-3.0.xml") $o/members
return <country> {mondial/country[#id=$m/#country]/#name/string()} </country>} </members>
</organization>
)[position() < 10]
return $p
}
</results>
I am unable to access the ancestor node in this problem as I have got an id stored of a parameter and now I want to match the id of that parameter and get a name of the parameter.
I am not getting any output for this. I am not sure where I am going wrong.
XML FILE :-
The link for the xml file is https://raw.githubusercontent.com/kroell/hsrm-mi-2semester-markuplanguage/master/Abgabe2/Aufgabe2/mondial-3.0.xml
In the link you posted, the root element of the XML is mondial not users, so I'd use mondial in this answer. Notice that member of organization and country are linked by country id, so you can do as follow to get country name of every member element :
<f>
{
for $m in $o/members
return <g> {mondial/country[#id=$m/#country]/#name/string()} </g>
}
</f>
Here is the complete working query. Tested in http://www.xpathtester.com/xquery using XML from the link posted in question as input :
<a>
{
for $p in
(
for $o in /mondial/organization
where fn:count($o/members)
order by fn:count($o/members) descending
return <b>
<c> {$o/#name/string()} </c>
<d> {$o/#abbrev/string()} </d>
<e> {fn:count($o/members)} </e>
<f> {for $m in $o/members
return <g> {/mondial/country[#id=$m/#country]/#name/string()} </g>} </f>
</b>
)[position() < 10]
return $p
}
</a>
You don't define the desired output of your query so it's hard to tell you why your query isn't giving the desired output; but there are several things about the query that suggest it's completely wrong.
Firstly, $o is always a single <mem> element, so fn:count($o) is always 1, so the use of this expression in the where and order by clauses cannot have any useful effect.
Secondly, the expression used to produce the content of the <f> element looks all wrong because it doesn't depend in any way on the value of $o.
Consider the following XML snippet:
<doc>
<chapter id="1">
<item>
<para>some text here</para>
</item>
</chapter>
</doc>
In XQuery, I have a function that needs to do some things based on the ancestor chapter of a given "para" element that is passed in as a parameter, as shown in the stripped down example below:
declare function doSomething($para){
let $chapter := $para/ancestor::chapter
return "some stuff"
};
In that example, $chapter keeps coming up empty. However, if I write the function similar to the follwing (i.e., without using the ancestor axis), I get the desired "chapter" element:
declare function doSomething($para){
let $chapter := $para/../..
return "some stuff"
};
The problem is that I cannot use explicit paths as in the latter example because the XMl I will be searching is not guaranteed to have the "chapter" element as a grandparent every time. It may be a great-grandparent or great-great-grandparent, and so on, as shown below:
<doc>
<chapter id="1">
<item>
<subItem>
<para>some text here</para>
</subItem>
</item>
</chapter>
</doc>
Does anyone have an explanation as to why the axis doesn't work, while the explicit XPath does? Also, does anyone have any suggestions on how to solve this problem?
Thank you.
SOLUTION:
The mystery is now solved.
The node in question was re-created in another function, which had the result of stripping it of all of its ancestor information. Unfortunately, the previous developer did not document this wonderful, little function and has cost us all a good deal of time.
So, the ancestor axis worked exactly as it should - it was just being applied to a deceptive node.
I thank all of you for your efforts in answering my questions.
The ancestor axis does work fine. I suspect your problem is namespaces. The example you showed and that I ran (below) has XML without any namespaces. If your XML have a namespace then you would need to provide that in the ancestor XPath, like this: $para/ancestor:foo:chapter where in this case the prefix _foo_ is bound to the correct namespace for the chapter element.
let $doc := <doc>
<chapter id="1">
<item>
<para>some text here</para>
</item>
</chapter>
</doc>
let $para := $doc//para
return $para/ancestor::chapter
RESULT:
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="1">
<item>
<para>some text here</para>
</item>
</chapter>
These things almost always boil down to namespaces! As a daignostic to confirm 100% that namespace are not the issue, can you try:
declare function local:doSomething($para) {
let $chapter := $para/ancestor::*[local-name() = 'chapter']
return $chapter
};
This seems surprising to me; which XQuery implementation are you using? With BaseX, the following query...
declare function local:doSomething($para) {
let $chapter := $para/ancestor::chapter
return $chapter
};
let $xml :=
<doc>
<chapter id="1">
<item>
<para>some text here</para>
</item>
</chapter>
</doc>
return local:doSomething($xml//para)
...returns...
<chapter id="1">
<item>
<para>some text here</para>
</item>
</chapter>
I suspect namespaces too. If $para/../.. works but $para/parent::item/parent::chapter turns up empty, then you know it's a question of namespaces.
Look for an xmlns declaration at the top of your content, e.g.:
<doc xmlns="http://example.com">
...
</doc>
In your XQuery, you then need to bind that namespace to a prefix and use that prefix in your XQuery/XPath expressions, like this:
declare namespace my="http://example.com";
declare function doSomething($para){
let $chapter := $para/ancestor::my:chapter
return "some stuff"
};
What prefix you use doesn't matter. The important thing is that the namespace URI (http://example.com in the above example) matches up.
It makes sense that ../.. selects the element you want, because .. is short for parent::node() which selects the parent node regardless of its name (or namespace). Whereas ancestor::chapter will only select <chapter> elements that are not in a namespace (unless you have declared a default element namespace, which is usually not a good idea in XQuery because it affects both your input and your output).