Android - Parsing XML with XPath - java

First of all, thanks to all the people who's going to spend a little time on this question.
Second, sorry for my english (not my first language! :D).
Well, here is my problem.
I'm learning Android and I'm making an app which uses a XML file to store some info. I have no problem creating the file, but trying to read de XML tags with XPath (DOM, XMLPullParser, etc. only gave me problems) I've been able to read, at least, the first one.
Let's see the code.
Here is the XML file the app generates:
<dispositivo>
<id>111</id>
<nombre>Name</nombre>
<intervalo>300</intervalo>
</dispositivo>
And here is the function which reads the XML file:
private void leerXML() {
try {
XPathFactory factory=XPathFactory.newInstance();
XPath xPath=factory.newXPath();
// Introducimos XML en memoria
File xmlDocument = new File("/data/data/com.example.gps/files/devloc_cfg.xml");
InputSource inputSource = new InputSource(new FileInputStream(xmlDocument));
// Definimos expresiones para encontrar valor.
XPathExpression tag_id = xPath.compile("/dispositivo/id");
String valor_id = tag_id.evaluate(inputSource);
id=valor_id;
XPathExpression tag_nombre = xPath.compile("/dispositivo/nombre");
String valor_nombre = tag_nombre.evaluate(inputSource);
nombre=valor_nombre;
} catch (Exception e) {
e.printStackTrace();
}
}
The app gets correctly the id value and shows it on the screen ("id" and "nombre" variables are assigned to a TextView each one), but the "nombre" is not working.
What should I change? :)
Thanks for all your time and help. This site is quite helpful!
PD: I've been searching for a response on the whole site but didn't found any.

You're using the same input stream twice, but the second time you use it it's already at the end of file. You have to either open the stream once more or buffer it e.g. in a ByteArrayInputStream and reuse it.
In your case doing this:
inputSource = new InputSource(new FileInputStream(xmlDocument));
before this line
XPathExpression tag_nombre = xPath.compile("/dispositivo/nombre");
should help.
Be aware though that you should properly close your streams.

The problem is that you cannot re-use the stream-input-source multiple times - the first call to tag_id.evaluate(inputSource) already has read the input up to the end.
One solution would be to parse Document in advance:
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
Document document = documentBuilderFactory.newDocumentBuilder().parse(inputSource);
Source source = new DOMSource(document);
// evalute xpath-expressions on the dom source

Related

getDocument() constantly returns a null value

I am trying to parse an XML file using Java that lives on a network drive...I have reviewed lots of XML parsing info here but cannot find the answer I need... the problem is that the getDocument() routine constantly returns a null value even though the parser gets a accurate location and file name.
Here is the code...
String ThisXMLFile = XMLFileData.getPath();
DOMParser myXMLParser = new DOMParser();
myXMLParser.parse(ThisXMLFile);
Document doc = myXMLParser.getDocument();
Some notes:
I had to use getPath() as the getName() function did not return the fully qualified file name and path - the XML file lives on a network directory and that directory is mapped on my PC to the 'V' drive
I have imported all the required class header files for DOM objects
The variable names given above are real and accurate so if I have inadvertently used a reserved keyword in a variable declaration then please offer correction.
I have extensive programming experience in a few languages but this is my first real Java app.
all the lines of code and the variables above work, until I reach the last line and then getDocument() just sets the doc variable to null... which makes the rest of the program break.
I Believe that your are calling the wrong method... according to your code, you're executing: DOMParser.parse(systemId) when you need to call: DOMParser.parse(InputSource) ...
to create an InputSource you can can do this:
InputSource source = new InputSource(new FileInputStream(ThisXMLFile));
myXMLParser.parse(source);
Document doc = myXMLParser.getDocument();
NOTE: remember to close the opened FileInputStream!!!
XMLInputFactory XMLFactory = XMLInputFactory.newInstance();
XMLStreamReader XMLReader = XMLFactory.createXMLStreamReader(myXMLStream);
while(XMLReader.hasNext())
{
if (XMLReader.getEventType() == XMLStreamReader.START_ELEMENT)
{
String XMLTag = XMLReader.getLocalName();
if(XMLTag.equals("value"))
{
String idValue = XMLReader.getAttributeValue(null, "id");
if (idValue.equals(ElementName))
{
System.out.println(idValue);
XMLReader.nextTag();
System.out.println(XMLReader.getElementText());
}
}
}
XMLReader.next();
}
so this is the code I finally got to...it works and solves the issue of retrieving specific XML data fro a XML file. I wanted at first to use nodelists, elements, Documents, etc but those functions never did work for me... this one did - thanks to all for the answers given as they helped me think this one through...

remove elements from XML file in java

I have generated an xml file from an excel data base and it contains automatically an element called "offset". To make my new file match my needs, I want to remove this element using java.
here is the xml content:
<Root><models><id>2</id><modelName>Baseline</modelName><domain_id>2</domain_id><description> desctiption </description><years><Y2013>value1</Y2013><Y2014>value2</Y2014><Y2015>value3</Y2015><Y2016>value4</Y2016><Y2017>value5</Y2017></years><offset/></models></Root>
I made a code that reads(with a buffered reader) and writes the content in a new file and used the condition if:
while (fileContent !=null){
fileContent=xmlReader.readLine();
if (!(fileContent.equals("<offset/>"))){
System.out.println("here is the line:"+ fileContent);
XMLFile+=fileContent;
}
}
But it does not work
I would personally recommend using a proper XML parser like Java DOM to check and delete your nodes, rather than dealing with your XML as raw Strings (yuck). Try something like this to remove your 'offset' node.
File xmlFile = new File("your_xml.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(xmlFile);
NodeList nList = doc.getElementsByTagName("offset");
for (int i = 0; i < nList.getLength(); i++) {
Node node = nList.item(i);
node.getParentNode().removeChild(node);
}
The above code removes any 'offset' nodes in an xml file.
If resources/speed considerations are an issue (like when your_xml.xml is huge), you would be better off using SAX, which is faster (a little more code intensive) and doesn't store the XML in memory.
Once your Document has been edited you'll probably want to convert it to a String to parse to your OutputStream of choice.
Hope this helps.
Just try to replace the unwanted string with an empty string. It is quick... and seriously dirty. But might solve your problem quickly... just to reappear later on ;-)
fileContent.replace("<offset/>, "");
String sXML= "<Root><models><id>2</id><modelName>Baseline</modelName><domain_id>2</domain_id><description> desctiption </description><years><Y2013>value1</Y2013><Y2014>value2</Y2014><Y2015>value3</Y2015><Y2016>value4</Y2016><Y2017>value5</Y2017></years><offset/></models></Root>";
System.out.println(sXML);
String sNewXML = sXML.replace("<offset/>", "");
System.out.println(sNewXML);
String xml = "<Root><models><id>2</id><modelName>Baseline</modelName><domain_id>2</domain_id><description> desctiption </description><years><Y2013>value1</Y2013><Y2014>value2</Y2014><Y2015>value3</Y2015><Y2016>value4</Y2016><Y2017>value5</Y2017></years><offset/></models></Root>";
xml = xml.replaceAll("<offset/>", "");
System.out.println(xml);
In your original code that you included you have:
while (fileContent !=null)
Are you initializing fileContent to some non-null value before that line? If not, the code inside your while block will not run.
But I do agree with the other posters that a simple replaceAll() would be more concise, and a real XML API is better if you want to do anything more sophisticated.

How to convert a String to an XML object in Java

I get a SOAP message from a web service, and I can convert the response string to an XML file using the below code. This works fine. But my requirement is not to write the SOAP message to a file. I just need to keep this XML document object in memory, and extract some elements to be used in further processing. However, if I just try to access the document object below, it comes as empty.
Can somebody please tell me how I can convert a String to an in-memory XML object (without having to write to a file)?
String xmlString = new String(data);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
try
{
builder = factory.newDocumentBuilder();
// Use String reader
Document document = builder.parse( new InputSource(
new StringReader( xmlString ) ) );
TransformerFactory tranFactory = TransformerFactory.newInstance();
Transformer aTransformer = tranFactory.newTransformer();
Source src = new DOMSource( document );
Result dest = new StreamResult( new File( "xmlFileName.xml" ) );
aTransformer.transform( src, dest );
}
Remove the 5 last lines of code, and you'll just have the DOM document in memory. Store this document in some field, rather than in a local variable.
If that isn't sufficient, then please explain, with code, what you mean with "if I just try to access the document object below, it comes as empty".
JB Nizet is right, the first steps create a DOM out of xmlString. That will load your xmlString (or SOAP message) into an in-memory Document. What the following steps are doing (all the things related with the Transform) is to serialize the DOM to a file (xmlFileName.xml), which is not what you want to do, right?
When you said that your DOM is empty, I think you tried to print out the content of your DOM with document.toString(), and returned something like "[document: null]". This doesn't mean your DOM is empty. Actually your DOM contains data. You need now to use the DOM API to get access to the nodes inside your document. Try something like document.getChildNodes(), document.getElementsByTagName(), etc

Writing to an XML File in Java

I have an XML file of which I have an element as shown;
"<Event start="2011.12.12 13:45:00:0000" end="2011.12.12 13:47:00:0000" anon="89"/>"
I want to add another attribute "comment" and write it to this XML File giving;
"<Event start="2011.12.12 13:45:00:0000" end="2011.12.12 13:47:00:0000" anon="89" comment=""/>"
How would I go about doing this?
Thanks, Matt
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringElementContentWhitespace(true);
Document document = factory.newDocumentBuilder().parse(xmlFile);
Element eventElement = (Element)document.getElementsByTagName("Event").item(0);
eventElement.setAttribute("comment", "");
FYI: I've use DOM framework here org.w3c.dom.*
Use setAttribute method to add attribute,
// Add an attribute
element.setAttribute("newAttrName", "attrValue");
Use the following method to write to XML file,
// This method writes a DOM document to a file
public static void writeXmlFile(Document doc, String filename) {
try {
// Prepare the DOM document for writing
Source source = new DOMSource(doc);
// Prepare the output file
File file = new File(filename);
Result result = new StreamResult(file);
// Write the DOM document to the file
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.transform(source, result);
} catch (TransformerConfigurationException e) {
} catch (TransformerException e) {
}
}
Parse the file, add the attribute and write it back to disk.
There is plenty of frameworks that can do this. The DOM framework in Java is probably the first thing you should look at.
Using DOM, as suggested in previous answers, is certainly reasonable for this particular problem, which is relatively simple.
However, I have found that JDOM is generally much easier to use when you want to parse and/or modify XML files. Its basic approach is to load the entire file into an easy to use data structure. This works well unless your XML file is very very large.
For more info go to http://www.jdom.org/

Parsing an XML file without root in Java

I have this XML file which doesn't have a root node. Other than manually adding a "fake" root element, is there any way I would be able to parse an XML file in Java? Thanks.
I suppose you could create a new implementation of InputStream that wraps the one you'll be parsing from. This implementation would return the bytes of the opening root tag before the bytes from the wrapped stream and the bytes of the closing root tag afterwards. That would be fairly simple to do.
I may be faced with this problem too. Legacy code, eh?
Ian.
Edit: You could also look at java.io.SequenceInputStream which allows you to append streams to one another. You would need to put your prefix and suffix in byte arrays and wrap them in ByteArrayInputStreams but it's all fairly straightforward.
Your XML document needs a root xml element to be considered well formed. Without this you will not be able to parse it with an xml parser.
One way is to provide your own dummy wrapper without touching the original 'xml' (the not well formed 'xml') Need the word for that:
Syntax
<!DOCTYPE some_root_elem SYSTEM "/home/ego/some.dtd"
[
<!ENTITY entity-name "Some value to be inserted at the entity">
]
Example:
<!DOCTYPE dummy [
<!ENTITY data SYSTEM "http://wherever-my-data-is">
]>
<dummy>
&data;
</dummy>
You could use another parser like Jsoup. It can parse XML without a root.
I think even if any API would have an option for this, it will only return you the first node of the "XML" which will look like a root and discard the rest.
So the answer is probably to do it yourself. Scanner or StringTokenizer might do the trick.
Maybe some html parsers might help, they are usually less strict.
Here's what I did:
There's an old java.io.SequenceInputStream class, which is so old that it takes Enumeration rather than List or such.
With it, you can prepend and append the root element tags (<div> and </div> in my case) around your no-root XML stream. (You shouldn't do it by concatenating Strings due to performance and memory reasons.)
public void tryExtractHighestHeader(ParserContext context)
{
String xhtmlString = context.getBody();
if (xhtmlString == null || "".equals(xhtmlString))
return;
// The XHTML needs to be wrapped, because it has no root element.
ByteArrayInputStream divStart = new ByteArrayInputStream("<div>".getBytes(StandardCharsets.UTF_8));
ByteArrayInputStream divEnd = new ByteArrayInputStream("</div>".getBytes(StandardCharsets.UTF_8));
ByteArrayInputStream is = new ByteArrayInputStream(xhtmlString.getBytes(StandardCharsets.UTF_8));
Enumeration<InputStream> streams = new IteratorEnumeration(Arrays.asList(new InputStream[]{divStart, is, divEnd}).iterator());
try (SequenceInputStream wrapped = new SequenceInputStream(streams);) {
DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Document xmlDocument = builder.parse(wrapped);
From here you can do whatever you like, but keep in mind the extra element.
XPath xPath = XPathFactory.newInstance().newXPath();
}
catch (Exception e) {
throw new RuntimeException("Failed parsing XML: " + e.getMessage());
}
}

Categories

Resources