Java: Performance of reading repeatedly from an XML file - java

I am working on a project using Java and JavaFX. i do impliment a methode to allow user to change between language. But i feel that it can be slow somehow.
I use an XML file to store all texts in it.
I creat the function "readXML" to read a called text from the XML.
I use this function so many times to complete the strings in the
scene.
I think that this is wrong because i read that reading from an XML file repeatedly and successively can slow my application. also the XML file is getting bigger and bigger while adding other scenes. that why i need help in this.
I still working on the project and my codes work right fine rigth now but it show a little slowing in every scene change (between 500ms and 1500ms depending on strings). i am afraid that this little time become bigger on next steps.
Here is the class ReadXMLFile.class that contain readXML:
package modele;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import java.io.File;
/**
* Créer par Malek Boubakri le 27/07/2015 à 20:37.
*/
public class ReadXMLFile {
public static String readXML(String name,int lang) {
String res = "";
try {
File fXmlFile = new File("res/files/strings.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(fXmlFile);
doc.getDocumentElement().normalize();
NodeList nList = doc.getElementsByTagName("lang");
Node nNode = nList.item(lang);
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
Element eElement = (Element) nNode;
res=eElement.getElementsByTagName(name).item(0).getTextContent();
}
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
}
and this is how i use it:
....
lbl_elev_opt1.setText(ReadXMLFile.readXML("lbl_elev_list",SettingDialer.langID));
lbl_elev_opt2.setText(ReadXMLFile.readXML("lbl_elev_edit",SettingDialer.langID));
lbl_elev_opt3.setText(ReadXMLFile.readXML("lbl_elev_add",SettingDialer.langID));
lbl_elev_opt4.setText(ReadXMLFile.readXML("lbl_elev_del",SettingDialer.langID));
lbl_ens_opt1.setText(ReadXMLFile.readXML("lbl_ens_list",SettingDialer.langID));
lbl_ens_opt2.setText(ReadXMLFile.readXML("lbl_ens_edit",SettingDialer.langID));
lbl_ens_opt3.setText(ReadXMLFile.readXML("lbl_ens_class",SettingDialer.langID));
lbl_ens_opt4.setText(ReadXMLFile.readXML("lbl_ens_exam",SettingDialer.langID));
lbl_cal_opt1.setText(ReadXMLFile.readXML("lbl_cal_in",SettingDialer.langID));
lbl_cal_opt2.setText(ReadXMLFile.readXML("lbl_cal_add",SettingDialer.langID));
lbl_cal_opt3.setText(ReadXMLFile.readXML("lbl_cal_edit",SettingDialer.langID));
lbl_arch_opt1.setText(ReadXMLFile.readXML("lbl_arch_rech",SettingDialer.langID));
lbl_arch_opt2.setText(ReadXMLFile.readXML("lbl_arch_add",SettingDialer.langID));
lbl_arch_opt3.setText(ReadXMLFile.readXML("lbl_arch_edit",SettingDialer.langID));
lbl_doc_opt1.setText(ReadXMLFile.readXML("lbl_doc_off",SettingDialer.langID));
lbl_doc_opt2.setText(ReadXMLFile.readXML("lbl_doc_dip",SettingDialer.langID));
lbl_doc_opt3.setText(ReadXMLFile.readXML("lbl_doc_aut",SettingDialer.langID));
at lease 100 other uses....
Please if anything is blur just comment! waiting for your help..and thanks..
(Sorry for my bad, strange english)

That's because you are reading and parsing the whole xml file everytime you need to access a node from it.
So to fix this I moved the block in readXML method that reads same file whatever parameter it takes under init method. So you read xml file and initialize Document for once and use same Document instance for repeating calls without reading same file over and over again. You can just replace your code with below without any changes in other classes.
public class ReadXMLFile {
private static boolean _initialized = false;
private static Document _doc;
public static void init() {
if(_initialized) {
return;
}
try {
File fXmlFile = new File("res/files/strings.xml");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
_doc = dBuilder.parse(fXmlFile);
_doc.getDocumentElement().normalize();
_initialized = true;
} catch (Exception e) {
e.printStackTrace();
}
}
public static String readXML(String name, int lang) {
if(!_initialized) {
init();
}
String res = "";
try {
NodeList nList = _doc.getElementsByTagName("lang");
Node nNode = nList.item(lang);
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
Element eElement = (Element) nNode;
res=eElement.getElementsByTagName(name).item(0).getTextContent();
}
} catch (Exception e) {
e.printStackTrace();
}
return res;
}
}

Related

How can I parse String containing XML tags , so that I can get value of all the sub tags

I have to parse a String containing XML tags like the one hard coded below so that I can get values of all the tags separately. Here when I am using
NodeList node = doc.getElementsByTagName("event");
It is returning value as "ajain1AnkitJain24-04-199223:09.08"
I want to retrieve value for each tag and store it separately in different variables.
Like for eg in this scenario I want to Store Value as :
String UID = ajain1
String FirstName = Ankit
String LastName = Jain
Date date = "24-04-1992 23:09.08"
Here is the Sample code I am working on.
package test;
import java.io.IOException;
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class Demo {
public static void main(String[] args) {
String xmldata = "<event><class></class><data><UID><![CDATA[ajain1]]></UID><FIRSTNAME><![CDATA[Ankit]]></FIRSTNAME><LASTNAME><![CDATA[Jain]]></LASTNAME><DATE><![CDATA[24-04-1992]]></DATE><TIME><![CDATA[23:09.08]]></TIME></data></event>";
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
try {
db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(xmldata));
try {
Document doc = db.parse(is);
//String message = doc.getDocumentElement().getTextContent();
//System.out.println(message);
NodeList node = doc.getElementsByTagName("event");
} catch (SAXException e) {
// handle SAXException
} catch (IOException e) {
// handle IOException
}
} catch (ParserConfigurationException e1) {
// handle ParserConfigurationException
}
// TODO Auto-generated method stub
}
}
Thanks and let me know if you require any more information.
A NodeList already is a list containing all requested nodes, but I have to admit, I find its implementation highly questionable. It's basically a node containing the requested nodes as children. Its implementation has very much nothing in common with other list implementations - it doesn't even implement the List interface. I don't exactly know how to handle [!CDATA], but to loop through all event tags you'd have to do something like this:
NodeList eventList = doc.getElementsByTagName("event");
for(int i = 0; i < eventList.getLength(); i++) {
Element eventElement = (Element) eventList.item(i);
// do some stuff with it
}
From this element, you can also use getElementsByTagName to get the information needed about first name and so on. And yes, it's likely to end up with many nested loops...

Document parsing shows null

I need help in the below concept.
I want to get attributes of xref node in the code. i.e id and its value, location and its value, type and its value.
I am passing xml as string. But the document shows null on parsing.
PLease help me in this.
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
public class GetAtrribute {
/**
* #param args
*/
public static void main(String[] args) {
String xml = "<xref id=\"19703675\" location=\"abstract\" type=\"external\">PubMed Abstract: http://www.abcd.nlm.nih.gov/...</xref>"; //Populated XML String....
GetAtrribute ga = new GetAtrribute();
try {
ga.getValues(xml);
} catch (Exception e) {
e.printStackTrace();
}
}
public String getValues(String xmlStr) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
xmlStr = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + xmlStr;
try {
builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(
xmlStr)));
Element element = document.getDocumentElement();
NodeList list = element.getElementsByTagName("xref");
if (list != null && list.getLength() > 0) {
NodeList subList = list.item(0).getChildNodes();
if (subList != null && subList.getLength() > 0) {
return subList.item(0).getNodeValue();
}
for (int count = 0; count < subList.getLength(); count++) {
System.out.println(subList.item(count).getNodeValue());
}
}
} catch (Exception e) {
e.printStackTrace();
}
return xmlStr;
}
}
Your problem is that when you run this line:
Element element = document.getDocumentElement();
you're actually selecting xref already, because its the only xml element. You could either wrap another object around xref, or just use the variable 'element' to get the details.
p.s. your class name is spelt wrong: GetAtrribute -> GetAttribute
I suggest you to use XPath to find data in your XML:
XPath xPath = XPathFactory.newInstance().newXPath();
Document baseDoc;
try (InputStream pStm = new ByteArrayInputStream(baseXmlString.getBytes("utf-8"))) {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
baseDoc = builder.parse(pStm);
} catch (SAXException | IOException | ParserConfigurationException ex) {
getLogger().error(null, ex);
return null;
}
try {
XPathExpression expression = xPath.compile(xPathExpression);
return (T) expression.evaluate(baseDoc, pathType);
} catch (XPathExpressionException ex) {
getLogger().error(null, ex);
}
return null;
For example take a look at here

Parsing xml document Java "cannot be resolved"

I'm going through a tutorial on how to parse an xml document with java and encountering a problem. I am getting the error "dom cannot be resolved" I know it has something to do with the way I am declaring the variables and being out of scope but I can't figure out how to fix it.
Any help would be greatly appreciated, I will post the relevant parts below:
package com.xmlparse;
import java.io.IOException;
import java.util.Iterator;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import com.entities.Employee;
public class XmlParser
{
private void parseXmlFile(){
//get the factory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
//Using factory get an instance of document builder
DocumentBuilder db = dbf.newDocumentBuilder();
//parse using builder to get DOM representation of the XML file
Document dom = db.parse("test.xml");
} catch(ParserConfigurationException pce) {
pce.printStackTrace();
} catch(SAXException se) {
se.printStackTrace();
} catch(IOException ioe) {
ioe.printStackTrace();
}
}
private void parseDocument() {
Document dom = db.parse("test.xml");
//get the root element
Element docEle = dom.getDocumentElement();
//get a nodelist of elements
NodeList nl = docEle.getElementsByTagName("Employee");
if(nl != null && nl.getLength() > 0) {
for(int i = 0 ; i < nl.getLength(); i++) {
//get the employee element
Element el = (Element)nl.item(i);
//get the Employee object
Employee e = getEmployee(el);
//add it to list
myEmpls.add(e);
}
}
}
As you are using DocumentBuilder db in different methods, you could declare db as a class member variable:
private DocumentBuilder db;
and initialize like so in parseXmlFile:
db = dbf.newDocumentBuilder();
You could change method signature like below and when call to it pass the created document builder instance.
private void parseDocument(DocumentBuilder db)

Parsing xml using Java

I am trying to parse a dom element.
Element element:
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>http://X/feed2</id>
<title>Sample Feed</title>
<entry>
<id>http://X/feed2/104</id>
<title>New Title</title>
</entry>
</feed>
I am trying to fetch the following entry:
<entry>
<id>http://top.cs.vt.edu/libx2/vsony7#vt.edu/feed2/104</id>
<title>New Title</title>
</entry>
I am parsing the xml by using the xpath:
"/atom:feed/atom:entry[atom:id=\"http://X/feed2/104\"]"
But, I get an exception when I try to parse Dom Element. Can someone suggest a simple approach to achieve this in Java?
Please see my full code:
public static parseXml() {
String externalEntryIdUrl = "http://theta.cs.vt.edu/~rupen/thirtylibapps/137";
String externalFeedUrl = StringUtils.substringBeforeLast(externalEntryIdUrl, "/");
try {
URL url = new URL(externalFeedUrl);
InputStream externalXml = new BufferedInputStream(url.openStream());
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(externalXml);
Element externalFeed = doc.getDocumentElement();
String atomNameSpace = "xmlns:atom=\"http://www.w3.org/2005/Atom\"";
String entryIdPath = String.format("//%s:entry[%s:id=%s]", atomNameSpace, atomNameSpace, externalEntryIdUrl);
Element externalEntry = (Element) XPathSupport.evalNode(entryIdPath, externalFeed);
} catch (Exception ex) {
// Throw exception
}
}
static synchronized Node evalNode(String xpathExpr, Node node) {
NodeList result = evalNodeSet(xpathExpr, node);
if (result.getLength() > 1)
throw new Error ("More than one node for:" + xpathExpr);
else if (result.getLength() == 1)
return result.item(0);
else
return null;
}
static synchronized NodeList evalNodeSet(String xpathExpr, Node node) {
try {
static XPath xpath = factory.newXPath();
xpath.setNamespaceContext(context);
static NamespaceContext context = new NamespaceContext() {
private Map<String, String> prefix2URI = new HashMap<String, String>();
{
prefix2URI.put("libx", "http://libx.org/xml/libx2");
prefix2URI.put("atom", "http://www.w3.org/2005/Atom");
}
};
XPathExpression expr = xpath.compile(xpathExpr);
Object result = expr.evaluate(node, XPathConstants.NODESET);
return (NodeList)result;
} catch (XPathExpressionException xpee) {
throw new Error ("An xpath expression exception: " + xpee);
}
}
SEVERE: >>java.lang.Error: An xpath expression exception: javax.xml.xpath.XPathExpressionException
You can use SAX parser.
Here is a example for SAX parsing http://www.mkyong.com/java/how-to-read-xml-file-in-java-sax-parser/
You could leverage a NamespaceContextand do something like the following:
package forum9059851;
import java.io.FileInputStream;
import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;
import javax.xml.xpath.*;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
public class Demo {
public static void main(String[] args) {
try {
XPathFactory xpf = XPathFactory.newInstance();
XPath xp = xpf.newXPath();
xp.setNamespaceContext(new MyNamespaceContext());
XPathExpression xpe = xp.compile("ns:feed/ns:entry");
FileInputStream xmlStream = new FileInputStream("src/forum9059851/input.xml");
InputSource xmlInput = new InputSource(xmlStream);
Element result = (Element) xpe.evaluate(xmlInput, XPathConstants.NODE);
System.out.println(result);
} catch (Exception ex) {
// Throw exception
}
}
private static class MyNamespaceContext implements NamespaceContext {
public String getNamespaceURI(String prefix) {
if("ns".equals(prefix)) {
return "http://www.w3.org/2005/Atom";
}
return null;
}
public String getPrefix(String namespaceURI) {
return null;
}
public Iterator getPrefixes(String namespaceURI) {
return null;
}
}
}
If you don't want to reinvent the wheel and want to parse feed data I would recommend going with the already available Rome library.
I figured that I didn't set the namespace awareness while fetching the xml from a URL.
So,
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
Doing so fixes my issue. Without doing this, setting the namespace context for XPathFactory instance while parsing the xml as shown in my example doesn't work by itself.

Parsing string of xml android

I'm having a little trouble parsing a string of xml called responseText in android. The xml is fully valid and has the following structure:
<plan>
<entry>
<name>john</name>
<address>uk</address>
</entry>
<entry>
<name>joe</name>
<address>usa</address>
</entry>
</plan>
The code I am using to parse the String is as follows:
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(responseText));
Document doc = db.parse(is);
NodeList nodes = doc.getElementsByTagName("entry");
for (int i = 0; i < nodes.getLength(); i++) {
Element element = (Element) nodes.item(i);
NodeList name = ((Document) element)
.getElementsByTagName("name");
Element line = (Element) name.item(0);
Toast.makeText(Containers.this,
getCharacterDataFromElement(line), Toast.LENGTH_SHORT)
.show();
NodeList title = ((Document) element)
.getElementsByTagName("address");
line = (Element) title.item(0);
Toast.makeText(Containers.this,
getCharacterDataFromElement(line), Toast.LENGTH_SHORT)
.show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static String getCharacterDataFromElement(Element e) {
Node child = ((Node) e).getFirstChild();
if (child instanceof CharacterData) {
CharacterData cd = (CharacterData) child;
return cd.getData();
}
return "?";
}
I'm just using simple toasts to print the xml data to screen. However, i get an IOexception when I enter the for loop. Any idea what is wrong?
Are you importing the types from the correct packages? Something like
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import android.sax.Element; // Wrong Element class
Change the last import to
import org.w3c.dom.Element;
and try again.
I don't see anything that could cause an IOException inside the loop.
However, are you sure you can just go and cast an Element into a Document? At any rate you shouldn't need to anyways, since Element also has the getElementsByTagName method.
Try adding an xml declaration at the start of your string.

Categories