I am using a oracle sql database to carryout sql queries with xpath expressions – I have created an XML file which contains data relating to a film
The XPath expression you're looking for (not the SQL expression) is:
count(/film/directors/director)
which result should be 1 with your example XML file.
If you want to check if it's 2, use
count(/film/directors/director) = 2
which should return FALSE with your XML file.
First, you obviously know you need to use xPath to query the XML file, but you seem to have failed to understand what xPath is or how it should be used.
My first suggestion would be to go a read up on xPath and xPath in Java because it has nothing to do with the SQL.
I then did a quick search on "java xpath count" and come across a number of excellent examples, but based on XPath count() function, I went about testing your document with...
try {
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance();
DocumentBuilder b = f.newDocumentBuilder();
// This is your document in a file
Document d = b.parse(new File("Test.xml"));
d.getDocumentElement().normalize();
String expression = "//film[count(directors)=1]";
XPath xPath = XPathFactory.newInstance().newXPath();
Object result = xPath.compile(expression).evaluate(d, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
System.out.println(nodes.getLength());
for (int i = 0; i < nodes.getLength(); i++) {
Node node = nodes.item(i);
System.out.println("Found " + node.getTextContent());
}
} catch (ParserConfigurationException | SAXException | IOException | XPathExpressionException | DOMException exp) {
exp.printStackTrace();
}
This basically listed the film node (found one match) ... but, why did you produce a result?! Look at the query, //film[count(directors)=1], it's listing all film matches with a one director, because I want to test the query. Change it to //film[count(directors)=2] and it will return a result of zero matches based on your example.
I would highly recommend that you pause for a moment and become more familiar with what xPath is and how it works before you continue
Related
Im building an application that will taka a word from user and then scan file using XPath returning true or false depending on wheather the word was found in that file or not.
I have build following class that implements XPath, but i am either missunderstanding how it should work or there is something wrong with my code. Can anyone explain to me how to use Xpath to make full file search?
public XPath() throws IOException, SAXException, ParserConfigurationException, XPathExpressionException {
FileInputStream fileIS = new FileInputStream("text.xml");
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(fileIS);
XPathFactory xPathfactory = XPathFactory.newInstance();
javax.xml.xpath.XPath xPath = xPathfactory.newXPath();
XPathExpression expr = xPath.compile("//text()[contains(.,'java')]");
System.out.println(expr.evaluate(xmlDocument, XPathConstants.NODESET));
}
And the xml file i am currently testing on.
<?xml version="1.0"?>
<Tutorials>
<Tutorial tutId="01" type="java">
<title>Guava</title>
<description>Introduction to Guava</description>
<date>04/04/2016</date>
<author>GuavaAuthor</author>
</Tutorial>
<Tutorial tutId="02" type="java">
<title>XML</title>
<description>Introduction to XPath</description>
<date>04/05/2016</date>
<author>XMLAuthor</author>
</Tutorial>
</Tutorials>
Found the solution, i was missing correct display of the found entries and as someone pointed out in comment 'java' is in arguments and i want to scan only text fields so it would be never found, after adding following code and changing the word my app will look for, application works
Object result = expr.evaluate(xmlDocument, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
for (int i = 0; i < nodes.getLength(); i++) {
System.out.println(nodes.item(i).getNodeValue());
}
Your XPath is searching the text() nodes, but the word java appears in the #type attribute (which is not a text() node).
If you want to search for the word in both text() and #* then you could use a union | operator and check for either/both containing that word:
//text()[contains(. ,'java')] | //#*[contains(., 'java')]
But you might also want to scan comment() and processing-instruction(), so could generically match on node() and then in the predicate test:
//node()[contains(. ,'java')] | //#*[contains(., 'java')]
With XPath 2.0 or greater, you could use:
//node()[(.|#*)[contains(., 'java')]]
How can i get XML value by attribute for the below XML:
I have tried:
String xml = "<Info><document><document>234doc</document></document></Info>";
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder()
.parse(new InputSource(new StringReader(xml)));
NodeList errNodes = doc.getElementsByTagName("error");
if (errNodes.getLength() > 0) {
Element err = (Element)errNodes.item(0);
} else {
Node value = doc.getElementsByTagName("document").item(0);
out.println(value);
}
I am looking for the output: "234doc". But I am not sure how do get the value. Can any one please suggest?
This is not a rocket science. You should debug your code, explore classes you might already know (Document, NodeList, Node, Element) and understand your xml structure (for more info look here). One way to achieve your result is:
System.out.println(doc.getChildNodes().item(0).getTextContent());
I have an XML file and reading the information using Xpath, I want to read the 'listings_Id' and 'budget_remaining' together.
XML example
<ads>
<ad>
<listing_ids>
<listing_id>2235</listing_id>
<listing_id>303</listing_id>
<listing_id>394</listing_id>
</listing_ids>
<reference_id>11</reference_id>
<net_ppe>0.55</net_ppe>
<budget_remaining>50000.0</budget_remaining>
</ad>
<ad>
<listing_ids>
<listing_id>2896</listing_id>
</listing_ids>
<reference_id>8</reference_id>
<net_ppe>1.5</net_ppe>
<budget_remaining>1.3933399</budget_remaining>
</ad>
</ads>
I want to output it to a CSV file as the following
ListingId,BudgetRemaining
2235,50000
303,50000
394,50000
2896,1.39
Using the code
String expression = "/ads/ad/listing_ids/listing_id";
System.out.println(expression);
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(docum, XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
System.out.println(nodeList.item(i).getFirstChild().getNodeValue());
}
String expression1 = "/ads/ad/budget_remaining";
System.out.println(expression1);
NodeList nodeList1 = (NodeList) xPath.compile(expression1).evaluate(docum, XPathConstants.NODESET);
for (int i = 0; i < nodeList1.getLength(); i++) {
System.out.println(nodeList1.item(i).getFirstChild().getNodeValue());
}
Output
/ads/ad/listing_ids/listing_id
2235
303
394
2896
/ads/ad/budget_remaining
50000.0
1.3933399
Desired Output
2235,50000.0
303,50000.0
2896,50000.0
2896,1.3933399
How to read the XML using Xpath or any other method? I want the 'listing_ids' and 'budget_ remaining' to be read together for each 'Listing Id' like
303,50000
Please help me-new to Java.
It may be easier for you to use jaxb to parse the XML into a list of ads.
You can then reference your Java list
I would suggest using XQuery, which unlike XPath can return structured results. (Or XPath 2.0, but if you're going to XPath 2.0 then you might as well go all the way to XQuery).
The relevant query is
string-join(
for $n in /ads/ad/listing_ids/listing_id
return $n/concat(., ',', ../../budget_remaining),
'
'
)
This will return the required output as a single string.
I have a list of objects which contain one XML String field. I have to execute an SQL like query for that field, and get a sub list that satisfies the values. I am trying to use XPath.
Firstly, I can't figure out the XPath string to achieve this. Secondly, there might be a better way of doing this. I tried searching through SO but the answers don't really address this problem
Details
I have a list of books:
List <Books> allBooks;
The Book class can have an id and details fields. The details is XML.
class Book
{
String id;
String details; //XML
}
Here is a sample of the details xml String:
<book>
<name>Harry Potter and the sorcerer's stone</name>
<author>J K Rowling</author>
<genre>fantasy</genre>
<keyword>wizard</keyword>
<keyword>british</keyword>
<keyword>hogwarts</keyword>
<price>25</price>
</book>
So, uptil here it is all set in stone. It is part of existing code and I cannot change that design.
My work is to take the list allBooks & run a query through it, the logic of which is:
WHERE author = "J K Rowling" AND
genre = "fantasy" AND
(keyword = "wizard" OR keyword="hogwarts")
I considered throwing this data in a DB to run an actual query, but since the list will only contain a couple of hundred records, the overhead of connection, loading data etc is not worth it.
Anyone know how to do this through XPath? Any better way of doing this?
We need book records
//book
with author "J K Rowling"
//book[author = "J K Rowling"]
and genre is "fantasy"
//book[author = "J K Rowling" and genre = "fantasy"]
and keyword is "wizard" or "hogwarts"
//book[author = "J K Rowling" and genre = "fantasy" and (keyword = "wizard" or keyword = "hogwarts")]
You need to build the XPath queries first. I recommend referring to a previous answer for those (hoaz has a good listing here). Then you need to write the code to compile the query and evaluate it. Example:
public List<Book> findBookInformation(List<Books> books)
throws ParserConfigurationException, SAXException,
IOException, XPathExpressionException {
List<Book> foundBooks = new ArrayList<Book>(); // books matching criteria
for (Book book : books) {
DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true); // never forget this!
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(book.details))); // parse details XML into a Doc object
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
//using one of the query examples
XPathExpression expr = xpath.compile("/book[author = \"J K Rowling\" and genre = \"fantasy\" and (keyword = \"wizard\" or keyword = \"hogwarts\")]");
Object result = expr.evaluate(doc, XPathConstants.NODESET);
NodeList nodes = (NodeList) result;
if (null != nodes && nodes.getLength() > 0) {
foundBooks.add(book); // add to your return list
}
}
return foundBooks;
}
You could extend a method like this to take in your query arguments to dynamically build your XPath query, but this should give you the basic idea.
Assume the Books is the root
/Books/Book[(author = "J K Rowling") and (genre = "fantasy") and (keyword = "wizard" or keyword = "hogwarts")]
private void parseXml(String urlPath) throws Exception {
URL url = new URL(urlPath);
URLConnection connection = url.openConnection();
DocumentBuilder db = DOCUMENT_BUILDER_FACTORY.newDocumentBuilder();
final Document document = db.parse(connection.getInputStream());
XPath xPathEvaluator = XPATH_FACTORY.newXPath();
XPathExpression nameExpr = xPathEvaluator.compile("rss/channel/item/title");
NodeList trackNameNodes = (NodeList) nameExpr.evaluate(document, XPathConstants.NODESET);
for (int i = 0; i < trackNameNodes.getLength(); i++) {
Node trackNameNode = trackNameNodes.item(i);
System.out.println(String.format("Blog Entry Title: %s" , trackNameNode.getTextContent()));
XPathExpression artistNameExpr = xPathEvaluator.compile("rss/channel/item/content:encoded");
NodeList artistNameNodes = (NodeList) artistNameExpr.evaluate(trackNameNode, XPathConstants.NODESET);
for (int j=0; j < artistNameNodes.getLength(); j++) {
System.out.println(String.format(" - Artist Name: %s", artistNameNodes.item(j).getTextContent()));
}
}
}
I have this code for parsing the title and content from the default wordpress xml, the only problem is that when I try to get the content of the blog entry, the xml tag is: <content:encoded> and I do not understand how to retrieve this data ?
The tag <content:encoded> means an element with the name encoded in the XML namespace with the prefix content. The XPath evaluator is probably unable to resolve the content prefix to it's namespace, which I think is http://purl.org/rss/1.0/modules/content/ from a quick Google.
To get it to resolve, you'll need to do the following:
Ensure your DocumentBuilderFactory has setNamespaceAware( true ) called on it after construction, otherwise all namespaces are discarded during parsing.
Write an implementation of javax.xml.namespace.NamespaceContext to resolve the prefix to it's namespace (doc).
Call XPath#setNamespaceContext() with your implementation.
You could also try to use XStream, wich is a good and easy to use XML parser. Makes you have almost no work for parsing known XML structures.
PS: Their site is currently offline, use Google Cache to see it =P