Exception while evaluating simple XPath expression - java

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);

Related

Special characters creates problem while writing xml

first of all please excuse my shallow understanding into coding as I am a business analyst. Now my question. I am writing java code to convert a csv into xml. I am able to read csv successfully into objects. However, while writing the xml, when special a space or "=" is encounteredan error is thrown.
Piece of the problematic code, I have imporovised the value in create element just to highlight the problem. In actual I am getting this value from an object:-
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder();
Document xmlDocument= documentBuilder.newDocument();
Element root = xmlDocument.createElement("Media NationalGroupId="8" AllFTA="1002" AllSTV="1001");
xmlDocument.appendChild(root);
My xml should look something like this
<Media DateCreated="20200224 145251" NationalGroupId="8" AllFTA="1002" AllSTV="1001" AllTV="1000" NextId="1000000">
createElement should only receive Media as the argument.
To add the other attributes (DateCreated, NationalGroupId, etc), you need to call setAttribute on root, one by one.

Mockito: How to mock XML Document object to retrieve value from XPath

I am trying to mock a org.w3c.dom.Document object such that calling XPath.evaluate() should return a defined value, e.g., foo, as below:
Document doc = Mockito.mock(Document.class);
Mockito.when(XPathUtils.xpath.evaluate("/MyNode/text()", doc, XPathConstants.STRING)).thenReturn("foo");
I shall pass the doc object to the target method that will extract the textual contents of node MyNode as foo.
I have tried mocking nodes and setting in the doc object as follows:
Node nodeMock = mock(Node.class);
NodeList list = Mockito.mock(NodeList.class);
Element element = Mockito.mock(Element.class);
Mockito.when(list.getLength()).thenReturn(1);
Mockito.when(list.item(0)).thenReturn(nodeMock);
Mockito.when(doc.getNodeType()).thenReturn(Node.DOCUMENT_NODE);
Mockito.when(element.getNodeType()).thenReturn(Node.ELEMENT_NODE);
Mockito.when(nodeMock.getNodeType()).thenReturn(Node.TEXT_NODE);
Mockito.when(doc.hasChildNodes()).thenReturn(false);
Mockito.when(element.hasChildNodes()).thenReturn(true);
Mockito.when(nodeMock.hasChildNodes()).thenReturn(false);
Mockito.when(nodeMock.getNodeName()).thenReturn("MyNode");
Mockito.when(nodeMock.getTextContent()).thenReturn("MyValue");
Mockito.when(element.getChildNodes()).thenReturn(list);
Mockito.when(doc.getDocumentElement()).thenReturn(element);
But this is giving error like:
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: String cannot
be returned by hasChildNodes() hasChildNodes() should return boolean
Is my approach correct and I am missing just another mock, or should I approach it differently? Please help.
Don't mock types you don't own !, it's wrong.
To avoid repeating here's an answer that explains why https://stackoverflow.com/a/28698223/48136
EDIT : What I mean is that the code should have usable builder methods (either in the production or in the test classpath) that should be able to create a real Document whatever the source of that document, but certainly not a mock.
For exemple this factory method or builder could use the DocumentBuilder this way :
class FakeXMLBuilder {
static Document fromString(String xml) {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
return dBuilder.parse(new ByteArrayInputStream(xml.getBytes("UTF_8")));
}
}
Of course this has to be tailored to the need of the project, and this can be customized a lot more. In my current project we have a lot of test builders that can create objects, json, etc. For exemple :
Leg leg = legWithRandomId().contact("Bob").duration(234, SECONDS).build();
String leg = legWithRandomId().contact("Bob").duration(234, SECONDS).toJSON();
String leg = legWithRandomId().contact("Bob").duration(234, SECONDS).toXML();
Whatever leg = legWithRandomId().contact("Bob").duration(234, SECONDS).to(WhateverFactory.whateverFactory());
Use Jsoup.parse() with a test XML String. Something like the following should work, for testing some instance method I've assumed is testClassInstance.readFromConnection(String url):
// Turn a block of XML (static String or one read from a file) into a Document
Document document = Jsoup.parse(articleXml);
// Tell Mockito what to do with the Document
Mockito.when(testClassInstance.readFromConnection(Mockito.any()))
.thenReturn(document);
I'm used to referring to this as "mocking" a Document, but it's just creating and using a real Document, which is then used in mocking other methods. You can construct the xml version of document however you like, or use its regular setters to manipulate it for testing whatever your code is supposed to do with the file. You could also replace that mess of reading-the-file with a constant string, provided it's short enough to be manageable.

Parse a single Line of XML into a HashMap

I'm building an android app that communicates to a web server and am struggling with the following scenario:
Given ONE line of XML in a String eg:
"<test one="1" two="2" />"
I would like to extract the values into a HashMap so that:
map.get("one") = "1"
map.get("two") = "2"
I already can do this with a full XML document using the SAX Parser, this complains when i try to just give it the above string with a MalformedUrlException: Protocol not found
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
Document doc = null;
builder = factory.newDocumentBuilder();
doc = builder.parse("<test one="1" two="2" />"); //here
I realize some regex could do this but Id really rather do it properly.
The same behaviour can be found at http://metacpan.org/pod/XML::Simple#XMLin which is what the web server uses.
Can anyone help? Thanks :D
DocumentBuilder.parse(String) treats the string as a URL. Try this instead:
Document doc = builder.parse(new InputSource(new StringReader(text)));
(where text contains the XML, of course).

XML parsing issue with '&' in element text

I have the following code:
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(inputXml)));
And the parse step is throwning:
SAXParseException: The entity name must immediately follow
the '&' in the entity reference
due to the following '&' in my inputXml:
<Line1>Day & Night</Line1>
I'm not in control of in the inbound XML. How can I safely/correctly parse this?
Quite simply, the input "XML" is not valid XML. The entity should be encoded, i.e.:
<Line1>Day & Night</Line1>
Basically, there's no "proper" way to fix this other than telling the XML supplier that they're giving you garbage and getting them to fix it. If you're in some horrible situation where you've just got to deal with it, then the approach you take will likely depend on what range of values you're expected to receive.
If there's no entities in the document at all, a regex replace of & with & before processing would do the trick. But if they're sending some entities correctly, you'd need to exclude these from the matching. And on the rare chance that they actually wanted to send the entity code (i.e. sent & but meant &amp;) you're going to be completely out of luck.
But hey - it's the supplier's fault anyway, and if your attempt to fix up invalid input isn't exactly what they wanted, there's a simple thing they can do to address that. :-)
Your input XML isn't valid XML; unfortunately you can't realistically use an XML parser to parse this.
You'll need to pre-process the text before passing it to an XML parser. Although you can do a string replace, replacing '& ' with '& ', this isn't going to catch every occurrence of & in the input, but you may be able to come up with something that does.
I used Tidy framework before xml parsing
final StringWriter errorMessages = new StringWriter();
final String res = new TidyChecker().doCheck(html, errorMessages);
...
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = db.parse(new InputSource(new StringReader(addRoot(html))));
...
And all Ok
is inputXML a string? Then use this:
inputXML = inputXML.replaceAll("&\\s+", "&");

Using XPath on String in Android (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"

Categories