Why is my XPath selecting nothing? - java

My XML file
<classifications>
<classification sequence="1">
<classification-scheme office="" scheme="CS" />
<section>G</section>
<class>01</class>
<subclass>R</subclass>
<main-group>33</main-group>
<subgroup>365</subgroup>
<classification-value>I</classification-value>
</classification>
<classification sequence="2">
<classification-scheme office="" scheme="CS" />
<section>G</section>
<class>01</class>
<subclass>R</subclass>
<main-group>33</main-group>
<subgroup>3415</subgroup>
<classification-value>A</classification-value>
</classification>
<classification sequence="1">
<classification-scheme office="US" scheme="UC" />
<classification-symbol>324/300</classification-symbol>
</classification>
<classification sequence="2">
<classification-scheme office="US" scheme="UC" />
<classification-symbol>324/307</classification-symbol>
</classification>
</classifications>
I want to parse the value with following condition
required all the classification-symbol element value along with condition office="US"
I tried with below XPath,
NodeList usClassification = (NodeList)xPath.compile("//classifications//classification//classification-scheme[#office=\"US\"]//classification-symbol//text()").evaluate(xmlDocument, XPathConstants.NODESET);
but I'm getting an empty result set,
System.out.println(usClassification.getLength()); //its becomes zero

This XPath (written on two lines to ease readability),
/classifications/classification[classification-scheme/#office='US']
/classification-symbol/text()
will select the classification-symbol text of the classification elements with classification-scheme #office attribute value equal to US:
324/300
324/307
as requested.

classification-symbol is not a child of classification-scheme - they are siblings. Use following-sibling axis to get from "scheme" to "symbol" instead:
//classifications/classification/classification-scheme[#office=\"US\"]/following-sibling::classification-symbol/text()

Related

XPath expression with where statement

I have xml and I want to get, using xpath expression, text from Text node only if Text_2 contains elements. Is there any possibility? I couldn't find out any.
<List>
<Response>
<Node>
<SomeNode>
<Text>text</Text>
<Text_1>text_1</Text_1>
<Text_2 value_1="some value 1" value_2="some value 2" />
</SomeNode>
</Node>
</Response>
</List>
I tried to get Text_2 elements using //*[#value_1] but I stuck and do not have any other idea
Your text says "want to get, using xpath expression, text from Text node only if Text_2 contains elements", with your given sample that would be //SomeNode[Text_2/*]/Text. For some reasons I don't understand, however, in your sample the Text_2 element doesn't have any child elements.

Is there a single XPath expression that can get a value from two different locations?

I am using javax XPath to extract a value from some xml that can be returned from a service in one of two formats (don't ask me why), as shown below:
<item>
<attrQualOptMany name="someName">
<value>someValueICareAbout</value>
<value qual="101">someValueICareAbout</value>
</attrQualOptMany>
</item>
or
<item>
<attr name="someName">someValueICareAbout</attr>
</item>
Is there a single XPath expression that can pluck
someValueICareAbout
from the
<item></item>
element in either case? Since I don't know which xml format will be returned ahead of time, I am always having to resolve the value by evaluating two XPath expressions against the item element, as shown below. There are actually several attributes of the item that can exist in either form, and I would prefer not to have scores of if-else blocks in my code to extract them if at all possible:
XPath xpath = XPathFactory.newInstance().newXPath();
Element itemElement; //This element has already been populated by prior code
String value = xpath.compile("attrQualOptMany[#name='someName']/value/text()")
.evaluate(itemElement, XPathConstants.STRING);
if(value == null || value .isEmpty()) {
value = xpath.compile("attr[#name='someName']/text()")
.evaluate(itemElement, XPathConstants.STRING);
}
String eval = "attrQualOptMany[#name='someName']/value/text() | attr[#name='someName']/text()"
String value = xpath.compile(eval).evaluate(itemElement, XPathConstants.STRING);
Just use OR statement in your xpath and pick first result.
//attrQualOptMany[#name='someName']/value/text()|//attr[#name='someName']/text()
I tested above statement using sample input
<items>
<item>
<attrQualOptMany name="someName">
<value>someValueICareAbout</value>
<value qual="101">someValueICareAbout</value>
</attrQualOptMany>
</item>
<item>
<attr name="someName">someValueICareAbout</attr>
</item>
</items>
on http://www.freeformatter.com/xpath-tester.html#ad-output
and it works fine (returns 3 results). Be sure to return just first result (and stop checking rest of the document). Returning all matching elements (and then picking first one) would be wasteful.

how to add an attribute to an XML element

I am using the DOM parser. I have to parse the following XML:
<abc>
<type action="">
<code>test</code>
<value>001</value>
</type>
<type action="">
<code>test2</code>
<value>002</value>
</type>
</abc>
so, depending on the value field under the type field, I have to fill in the action attribute in the type field. I am a bit stumped. I am able to get the value of the value field, but I don't know how to go back and add the attribute.
Any help will be appreciated a lot!!!
thanks!
To go back, just save a reference to the type Element before you traverse to its value child. (assuming you visited it already).
to change the value, use the setAttribute() method.
edit:
Alternate method: from the value text node, call getParentNode() twice (once to get back to the value element & once to get back to the type element), then call setAttribute() after you do any necissary casting.
try something like
nodelist = doc.getElementsByTagName("value");
for (Element element : nodelist) {
Element parent = element.getParentNode()
parent.setAttribute("action", "attrValue");
}

Using Both Tagged And Untagged Data With XPath

I'm trying to parse some HTML using XPath in Java. Consider this HTML:
<td class="postbody">
<img src="...""><br />
<br />
<b>What is Blah?</b><br />
<br />
Blah blah blah
<br />
Note that "What Is Blah" is helpfully contained within a b tag and is therefore easily parseable. But "Blah blah blah" is out in the open, and so I can only pick it up by calling text() on its parent node.
Thing is, I need to go through this in sequence, putting the img down, then the bolded text, then the body text. It's important it ends up in order (it needn't be processed in order, if you can suggest a way that takes two passes).
So are there any suggestions for how, if I've got the above contained within a Java XPath node, I can go through it in turn and get what I need?
I think an SAX based parser would be a better tool for this problem. It's event based so you can parse your XML document in order.
But it's an XML parser so you'll need to have a valid XML document. I never used JTidy but it's a java port of the HTML Tidy, so hopefully it can help you to transform your (invalid) HTML documents to a valid XML.
Use this XPath expression evaluated with the parent of the provided XML fragment as the context node:
node()
This selects every node - child of the context node -- every element -child, every text-node-child, every comment-child and every PI (processing instruction) - child.
In case you want to exclude comments and PIs, use:
node()[not(self::comment() or self::processing-instruction)]
In case that in addition to this you don't want to select the whitespace-only-text-nodes, use:
node()
[not(self::comment() or self::processing-instruction)]
[not(self::text()[string-length() = 0])]

How do I select something with a blank namespace with Jaxen?

I have the following xml:
<config xmlns="http://www.someurl.com">
<product>
<brand>
<content />
</brand>
</product>
</config>
I'm reading it nicely into JDOM.
However, when I try to use Jaxen to grab the contents, I can't seem to get anything.
Here's an example of what doesn't seem to work:
XPath xpath = new JDOMXPath("config");
SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
namespaceContext.addNamespace("", "http://www.someurl.com");
xpath.setNamespaceContext(namespaceContext);
assert xpath.selectNodes(document).size() > 0 : "should find more than 0";
This assertion always fails.
What am I doing wrong?
You have to assign a prefix. Make that call addNamespace("hopfrog", "http://...");
Then make the XPath ("hopfrog:config");
Keep in mind that the prefixes in XML aren't part of the real data model. The real data model assigns a URL, possibly blank, to each element and attribute. You can use any prefix you want in XPath so long as it's bound to the right URL. Since the URL you want it blank, you bind a prefix to 'blank'.

Categories

Resources