xpath - How to get Method B to work? - xpath

Using the Schema of Method A with xpath to read and map the unbounded node (“detail”) is working to output multiple messages. The only issue is that designing the xsd schema the unbounded node must always be in a sequence.
In the Message Assignment object I am using, the instance XPath that I am trying to read and map is
XPathVar = System.String.Format(“
/* [local-name()=’header’ and namespace-uri()=’http://namespace’]
/* [local-name()=’detail’ and namespace-uri()=’http://namespace’] and
position() = {0}]”, nLoopCount)
If I don’t have the detail node straight after the header node than it fails throwing an exception similar to ‘contained a null value at the end of the construct block’. Is there any way to get Method B to work?
i.e
This method works!
[Method A]
<schema>
<header> (Node)
<detail> (Node) unbounded
<child elements>
</detail>
<additional info> (Node)
<child elements>
</additional info>
</header>
but this Does Not Work and throws an exception similar to ‘contained a null value at the end of the construct block’
[Method B]
<schema>
<header> (Node)
<additional info> (Node)
<child elements>
</additional info>
<detail> (Node) unbounded
<child elements>
</detail>
</header>
if there are other elements or Nodes separating the < header > and < detail > in a schema than I get the exception error.
Can anyone shed any light on this problem?

I think that you want to use this:
XPathVar = System.String.Format(“
/* [local-name()=’header’ and namespace-uri()=’http://namespace’]
/* [local-name()=’detail’ and namespace-uri()=’http://namespace’]
[position() = {0}]”, nLoopCount)
Explanation: The following often select equivalent sets:
/*[condition1 and condition2]
/*[condition1][condition2]
However, this breaks down when using position. Consider this expression:
/*[condition1 and position()=1]
It selects all elements for which both of the following are true:
condition1 is true
the element's context position is equal to one
However, this expression:
/*[condition1][position()=1]
...first selects all elements for which condition1 is true and then takes the first such element.
It's a subtle but important difference.

Related

Xpath - How to select a node but not its child nodes

I am trying to select a node but not any of its child nodes.
Example Input:
<Header attr1="Hello">
<child1> hello </child1>
<child2>world</child2>
</Header>
Expected Output: <Header attr1="Hello"> </Header>
Code:
Document xmlDoc = saxBuilder.build(inputStream);
Xpath x = XPath.newInstance("/Header");
eleMyElement = x.selectSingleNode(xmlDoc);
XMLOutputter output = new XMLOutputter();
output.outputString(eleMyElement) --> this is the output
I tried with /Header as XPath, it gives me the header along with child nodes.
You need to distinguish what is selected from what is displayed.
The XPath expression /Header selects one node only, the Header element. You say "it gives me", but what is "it"? Something is displaying the results of the XPath selection, and it is choosing to display the results by rendering the selected element with all its children. You need to look at the code that is displaying the result.
In this case you can simply do
eleMyElement.getContent().clear();
and all child nodes will be deleted.

How to get parent element with attribute using xpath

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::" =)

XPath to get parents with multiple children but only one type of child

I need an XPath (1.0) to get all parent nodes with multiple children but only one type of child (e.g., either <div> or <li> but not <div> and <li>). Any help? Thank you!
<doc>
<tom>
<janet />
</tom>
<dick>
<janet />
<jane />
</dick>
<harry>
<jane />
</harry>
</doc>
So for the above we should get tom and harry but not dick
Using the example as a reference, the following XPath 1.0 expression:
/doc/*[count(./*) = count(./*[name(.) = name(../*[1])])]
Will return all children of doc where the total number of children of that element equals the number of children with the same name as the first child of that element. Or, more simply put, all children have the same name aka 'type'.
However, the above will return nodes that have 0 or 1 children, so to restrict it to only those where there are multiple child nodes, we can use:
/doc/*[count(./*) = count(./*[name(.) = name(../*[1])]) and count(./*) > 1]
If you want to further restrict it so that all children have to be a certain element, for example jane, you could use: /doc/*[count(./*) = count(./*[name(.) = name(../*[1])]) and count(./*) > 1 and ./*[1] = ./jane[1]]

XPath: limit scope of result set

Given the XML
<a>
<c>
<b id="1" value="noob"/>
</c>
<b id="2" value="tube"/>
<a>
<c>
<b id="3" value="foo"/>
</c>
<b id="4" value="goo"/>
<b id="5" value="noob"/>
<a>
<b id="6" value="near"/>
<b id="7" value="bar"/>
</a>
</a>
</a>
and the Xpath 1.0 query
//b[#id=2]/ancestor::a[1]//b[#value="noob"]
The Xpath above returns both node ids 1 and 5. The goal is to limit the result to just node id=1 since it is the only #value="noob" element that is a descendant of the same <a> that (//b[#id=2]) is also a descendant of.
In other words, "Find all b elements who's value is "noob" that are descendants of the a element which also has a descendant whose id is 2, but is not the descendant of any other a element". How's that for convoluted? In practice the id number and values would be variable and there would hundreds of node types.
If the id=2, we would expect to return element id=1 not id=5 since it is contained in another a element. If the id=4, we would expect to return id=5, but not id=1 since it is not in the first ancestor a element as id=4.
Edit:
Based on the comments of Dimitre and Alejandro, I found this helpful blog entry explaining the use of count() with the | union operator as well as some other excellent tips.
Use:
//b[#value='noob']
[count(ancestor::a[1] | //b[#id=2]/ancestor::a[1]) = 1]
Explanation:
The second predicate assures that both b elements have the same nearest ancestor a.
Remember: In XPath 1.0 the test for node identity is:
count($n1 | $n2) = 1
First, this
is there some way to limit the result
set to the <b> elements that are ONLY
the children of the immediate <a>
element of the start node
(//b[#id=2])?
//b[#value='noob'][ancestor::a[1]/b/#id=2]
It's not the same as:
Starting at a node whose id is equal
to 2, find all the elements whose
value is "noob" that are descendants
of the immediate parent c element
without passing through another c
element
Wich is:
//c[b/#id=2]//*[.='noob'][ancestor::c[1][b/#id=2]]
Besides these expressions, when you are dealing with "context marks" you can use the set's membership test as in:
$node[count(.|$node-set)=count($node-set)]
I leave you its use for this case as an exercise...
//b[#id=2]/ancestor::a[1]//b[#value="noob" and not(ancestor::a[2]=//b[#id=2]/ancestor::a[1])] ?
that works only for your case though, not sure how generic it should be!

Using XQuery/XPath to get the attribute value of an element's parent node

Given this xml document:
<?xml version="1.0" encoding="UTF-8"?>
<mydoc>
<foo f="fooattr">
<bar r="barattr1">
<baz z="bazattr1">this is the first baz</baz>
</bar>
<bar r="barattr2">
<baz z="bazattr2">this is the second baz</baz>
</bar>
</foo>
</mydoc>
that is being processed by this xquery:
let $d := doc('file:///Users/mark/foo.xml')
let $barnode := $d/mydoc/foo/bar/baz[contains(#z, '2')]
let $foonode := $barnode/../../#f
return $foonode
I get the following error:
"Cannot create an attribute node (f) whose parent is a document node".
It seems that the ../ operation is sort of removing the matching nodes from the rest of the document such that it thinks it's the document node.
I'm open to other approaches but the selection of the parent depends on the child attribute containing a certain sub-string.
Cheers!
The query you have written is selecting the attribute f. However it is not legal to return an attribute node from an XQuery. The error is refering to the output document which here contains just an attribute (although this error message is misleading, as technically there is no output document here, there is just an attribute node that is returned).
You probably wanted to return the value of the attribute rather than the attribute itself
return data($foonode)

Resources