Using XPath on String in Android (JAVA) - java

I am looking for some examples of using xpath in Android? Or if anyone can share their experiences. I have been struggeling to make tail or head of this problem :-(
I have a string that contains a standard xml file. I believe I need to convert that into an xml document. I have found this code which I think will do the trick:
public static Document stringToDom(String xmlSource)
throws SAXException, ParserConfigurationException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
return builder.parse(new InputSource(new StringReader(xmlSource)));
}
Next steps
Assuming the code above is OK, I need to apply xpath to get values from cat: "/animal/mammal/feline/cat"
I look at the dev doc here: http://developer.android.com/reference/javax/xml/xpath/XPath.html and also look online, but I am not sure where to start!
I have tried to use the following code:
XPathFactory xPathFactory = XPathFactory.newInstance();
// To get an instance of the XPathFactory object itself.
XPath xPath = xPathFactory.newXPath();
// Create an instance of XPath from the factory class.
String expression = "SomeXPathExpression";
XPathExpression xPathExpression = xPath.compile(expression);
// Compile the expression to get a XPathExpression object.
Object result = xPathExpression.evaluate(xmlDocument);
// Evaluate the expression against the XML Document to get the result.
But I get "Cannot be resolved". Eclipse doesn't seem to be able to fix this import. I tried manually entering:
javax.xml.xpath.XPath
But this did not work.
Does anyone know any good source code that I can utilise, for Android platform? 1.5

What version of the SDK are you using? XPath was introduced in SDK 8(2.2). If you aren't building against that version then the class doesn't exist.

Rpond is correct:
javax.xml.xpath is introduced since API level 8 (Android 2.2).
Android 1.5. is only API Level 3.
References
package javax.xml.xpath -- "Since: API Level 8"
Description -- with examples
Android API levels -- "Platform Version Android 1.5 - API Level 3"

Related

Exception while evaluating simple XPath expression

Hi I have a problem with an XPath expression while trying to write a test.
I have a following fragment of code.
final String resultCode = xPath.compile(
"//*:Envelope/*:Body/ResultCode/text()")
.evaluate(responseEntity.getBody());
The responseEntity is returned by my mock. It consists of HttpStatus and proper response body in xml format. While executing the test I get this exception
Caused by: javax.xml.xpath.XPathExpressionException: Cannot locate an object model implementation for nodes of class java.lang.String
at net.sf.saxon.xpath.XPathExpressionImpl.evaluate(XPathExpressionImpl.java:321)
at net.sf.saxon.xpath.XPathExpressionImpl.evaluate(XPathExpressionImpl.java:396)
...
I am using saxon for this task, but to be honest I am not very familiar with it. Any suggestions what to check are welcome
Ok I've figured it out. Unfortunately such things happen when you have to fix somebody's code in a field you have no knowledge at all.
The issue was that a String was passed to the evaluate method while it expect one of NodeInfo, DOM, Document etc.
Also thanks to #paul trmbrth for fixing the xpath expression which was malformed too.
I changed the code to something like this:
InputSource source = new InputSource(new StringReader(responseEntity.getBody()));
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
final Document document = db.parse(source);
final String resultCode = xPath.compile(
"//*[local-name()='Envelope']/*[local-name()='Body']/*[local-name()='ResultCode']/text()")
.evaluate(document);

In Java how do I evaluate XPATH expression on XML using SAX Parser?

In Java how do I evaluate XPATH expression on XML using SAX Parser?
Need more dynamic way because the XML format is not fixed. So i should be able pass the following
xpath as string
xml as string / input source
Something like Utility.evaluate("/test/#id='123'", "")
Here is an exemple :
//First create a Document
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File("test.xml"));
//Init the xpath factory
XPath xPath = XPathFactory.newInstance().newXPath();
String expression = "/company/employee";
//read a nodelist using xpath
NodeList nodeList = (NodeList) xPath.compile(expression).evaluate(doc, XPathConstants.NODESET);
EDIT :
If you want to use a SAX parser, then you can't use the XPath object of Java, see https://docs.oracle.com/javase/7/docs/api/javax/xml/xpath/package-summary.html
The XPath language provides a simple, concise syntax for selecting nodes from an XML document. XPath also provides rules for converting a node in an XML document object model (DOM) tree to a boolean, double, or string value. XPath is a W3C-defined language and an official W3C recommendation; the W3C hosts the XML Path Language (XPath) Version 1.0 specification.
XPath started in life in 1999 as a supplement to the XSLT and XPointer languages, but has more recently become popular as a stand-alone language, as a single XPath expression can be used to replace many lines of DOM API code.
If you want to use SAX you can look at libs detailed in this question : Is there any XPath processor for SAX model? .
Although the mechanic of XPath does not really suit SAX. Indeed using a SAX parser won't create an XML tree in memory. Hence you can't use XPath efficiently because it won't see not loaded nodes.
Only a small subset of XPath is amenable to streamed evaluation, that is, evaluation on-the-fly while parsing the input document. There are therefore not many streaming XPath processor around; most of them are the product of academic research projects.
One thing you could try is Saxon-EE streamed XQuery. This is a small subset of XQuery that allows streamed executaion (it will allow expressions like your example). Details at
http://www.saxonica.com/documentation/#!sourcedocs/streaming/streamed-query
Oracle's XQuery processor for Java will "dynamically" stream path expressions:
https://docs.oracle.com/database/121/ADXDK/adx_j_xqj.htm#ADXDK99930
Specifically, there is information on streaming here, including an example:
https://docs.oracle.com/database/121/ADXDK/adx_j_xqj.htm#ADXDK119
But it will not stream using SAX. You must bind the input XML as either StAX, InputStream, or Reader to get streaming evaluation.
You can use a SAXSource with XPath using Saxon, but - and this is important - be aware that the underlying implementation will almost certainly still be loading and buffering some or all of the document in memory in order to evaluate the xpath. It probably won't be a full DOM tree (Saxon relies on its own structure called TinyTree, which supports lazy-loading and various other optimizations), so it's better than using most DOM implementations, but it still involves loading the document into memory. If your concern is memory load for large data sets, it probably won't help you much, and you'd be better off using one of the streaming xpath/xquery options suggested by others.
An implementation of your utility method might look something like this:
import java.io.StringReader;
import javax.xml.namespace.QName;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import org.xml.sax.InputSource;
import net.sf.saxon.xpath.XPathFactoryImpl;
public class XPathUtils {
public static Object evaluate(String xpath, String xml, QName returnType)
throws Exception {
SAXParser parser = (SAXParser) SAXParserFactory.newInstance()
.newSAXParser();
InputSource source = new InputSource(new StringReader(xml));
SAXSource saxSource = new SAXSource(parser.getXMLReader(), source);
XPath xPath = new XPathFactoryImpl().newXPath();
return xPath.evaluate(xpath, saxSource, returnType);
}
public static String xpathString(String xpath, String xml)
throws Exception {
return (String) evaluate(xpath, xml, XPathConstants.STRING);
}
public static boolean xpathBool(String xpath, String xml) throws Exception {
return (Boolean) evaluate(xpath, xml, XPathConstants.BOOLEAN);
}
public static Number xpathNumber(String xpath, String xml) throws Exception {
return (Number) evaluate(xpath, xml, XPathConstants.NUMBER);
}
public static void main(String[] args) throws Exception {
System.out.println(xpathString("/root/#id", "<root id='12345'/>"));
}
}
This works because the Saxon XPath implementation supports SAXSource as a context for evaluate(). Be aware that trying this with the built-in Apaache XPath implementation will throw an exception.

XPathFactory not working

I'm trying to use an XPathFactory to evaluate an expression in a Java application. But I'm getting a Saxon-specific error. At one time I used Saxon for some functionality, and to do that I had to set a system property:
System.setProperty("javax.xml.xpath.XPathFactory:" + NamespaceConstant.OBJECT_MODEL_SAXON,
"net.sf.saxon.xpath.XPathFactoryImpl");
XPathFactory xpf = XPathFactory.newInstance(NamespaceConstant.OBJECT_MODEL_SAXON);
However, now I just want to do some XML processing using the default DOM (org.w3c.dom.Document) and process with xpath, so Saxon isn't needed. But when I try to create an XPathFactory I still get the Saxon error message:
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: net/sf/saxon/lib/EnvironmentVariableResolver
at net.sf.saxon.xpath.XPathFactoryImpl.<init>(XPathFactoryImpl.java:26)
...
I even tried "resetting" the system property:
System.setProperty("javax.xml.xpath.XPathFactory:",
"org.apache.xpath.jaxp.XPathFactoryImpl");
XPathFactory factory = XPathFactory.newInstance();
And
System.setProperty("javax.xml.xpath.XPathFactory:",
"http://java.sun.com/jaxp/xpath/dom");
XPathFactory factory = XPathFactory.newInstance();
But that doesn't help, I still get the same error message.
How do I get rid of this in order to use the default XPathFactory again? (this has worked fine before I tried using Saxon)
As a workaround, you can explicitly instanciate the JDK factory (or Xerces's or Saxon's).
import org.apache.xpath.jaxp.XPathFactoryImpl
// import com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl
// import net.sf.saxon.xpath.XPathFactoryImpl
...
XPathFactory factory = new XPathFactoryImpl();
If possible, prefer the real Xerces implementation to the one found in the JDK. It is more reliable.
I have encounter the same question. Even no "System.setProperty" is called, jaxp will load saxon's xpath engine as default implementation provding saxon jar is on the classpath. Reference: namespace-unaware XPath expression fails if Saxon is on the CLASSPATH.
My solution: call saxon directly as: " XPathFactory _xFactory = new net.sf.saxon.xpath.XPathFactoryImpl();" and add jaxen-xxx.jar and xercesImpl.jar before saxon9e.jar on the classpath. Everything else remains its original state without call "System.setProperty". This works for me.
I also test another method as follows:
System.setProperty("javax.xml.xpath.XPathFactory:" +XPathConstants.DOM_OBJECT_MODEL, "net.sf.saxon.xpath.XPathFactoryImpl");
XPathFactory xFactory = XPathFactory.newInstance(XPathConstants.DOM_OBJECT_MODEL);
System.setProperty(XPathFactory.DEFAULT_PROPERTY_NAME +":" + XPathFactory.DEFAULT_OBJECT_MODEL_URI, " org.apache.xpath.jaxp.XPathFactoryImpl");
XPathFactory xFactory2 = XPathFactory.newInstance();
System.out.println(xFactory.toString());
System.out.println(xFactory2.toString());
The output:
net.sf.saxon.xpath.XPathFactoryImpl#71623278
com.sun.org.apache.xpath.internal.jaxp.XPathFactoryImpl#768b970c
Since Jaxp use apache's jaxen as its default xpath implementation, this method should work tool.
Since JAXP uses

Working with Xpath in android

In the past ive used xpath to find the value of specific nodes that came from an xml ducemnt from a URL. Now i want to use this same code but from an xml document that is stored locally on the android phone at say sdcard/images/xml/newxml.xml
Here is the old code that i would like to be able to implement to use this, i just cannot figure out how to use the local xml file instead of a URL.
URL url = new URL("UrlWentHere");
InputSource xml = new InputSource(url.openStream());
XPath xpath = XPathFactory.newInstance().newXPath();
datafromxml = xpath.evaluate("//forecast_conditions[1]/high/#data", xml);
I don't quite understand the question. Why not just URL url = new URL("sdcard/images/xml/newxml.xml"); - or does the problem have to do with the app's restricted access to the file system?

JDOM, XPath and Namespace Interactions

I'm having a very frustrating time extracting some elements from a JDOM document using an XPath expression. Here's a sample XML document - I'd like to remove the ItemCost elements from the document altogether, but I'm having trouble getting an XPath expression to evaluate to anything at the moment.
<srv:getPricebookByCompanyResponse xmlns:srv="http://ess.com/ws/srv">
<srv:Pricebook>
<srv:PricebookName>Demo Operator Pricebook</srv:PricebookName>
<srv:PricebookItems>
<srv:PricebookItem>
<srv:ItemName>Demo Wifi</srv:ItemName>
<srv:ProductCode>DemoWifi</srv:ProductCode>
<srv:ItemPrice>15</srv:ItemPrice>
<srv:ItemCost>10</srv:ItemCost>
</srv:PricebookItem>
<srv:PricebookItem>
<srv:ItemName>1Mb DIA</srv:ItemName>
<srv:ProductCode>Demo1MbDIA</srv:ProductCode>
<srv:ItemPrice>20</srv:ItemPrice>
<srv:ItemCost>15</srv:ItemCost>
</srv:PricebookItem>
</srv:PricebookItems>
</srv:Pricebook>
</srv:getPricebookByCompanyResponse>
I would normally just use an expression such as //srv:ItemCost to identify these elements, which works fine on other documents, however here it continually returns 0 nodes in the List. Here's the code I've been using:
Namespace ns = Namespace.getNamespace("srv","http://ess.com/ws/srv");
XPath filterXpression = XPath.newInstance("//ItemCost");
filterXpression.addNamespace(ns);
List nodes = filterXpression.selectNodes(response);
Where response is a JDOM element containing the above XML snippet (verified with an XMLOutputter). nodes continually has size()==0 whenever parsing this document. Using the XPath parser in Eclipse on the same document, this expression does not work either. After some digging, I got the Eclipse evaluator to work with the following expression: //*[local-name() = 'ItemCost'], however replacing the //srv:ItemCost in the Java code with this still produced no results. Another thing I noticed is if I remove the namespace declaration from the XML, //srv:ItemCost will resolve correctly in the Eclipse parser, but I can't remove it from the XML. I've been scratching my head for ours hours on this one now, and would really appreciate some nudging in the right direction.
Many thanks
Edit : Fixed code -
Document build = new Document(response);
XPath filterXpression = XPath.newInstance("//srv:ItemCost");
List nodes = filterXpression.selectNodes(build);
Strange, indeed... I tested on my side with jdom, and your snippet produced an empty list, the following works as intended:
public static void main(String[] args) throws JDOMException, IOException {
File xmlFile = new File("sample.xml");
SAXBuilder builder = new SAXBuilder();
Document build = builder.build(xmlFile);
XPath filterXpression = XPath.newInstance("//srv:ItemCost");
System.out.println(filterXpression.getXPath());
List nodes = filterXpression.selectNodes(build);
System.out.println(nodes.size());
}
It produces the output:
//srv:ItemCost
2

Categories