Please turn your phasers to "noob".
As a part of my Java Servlet, I make a call to a REST resource and accept the text file returned, as below:
// check to see if the file really exists (i.e. a session is in
// progress) or we need to create one
// this should save constantly hitting the server for a new file for
// every transaction.
if (fXmlFile.exists()) {
} else {
File collectionTree = new File(bscConnector.GetCollection());
PrintWriter xmlfile = new PrintWriter(directoryName + "/outputString.xml");
xmlfile.println(collectionTree);
xmlfile.close();
}
From there I run a search and replace on it to make it valid XML file so that I can actually run xpath queries against it:
SearchAndReplace sAndR = new SearchAndReplace();
// Swap the slashes so we can actually
// query the freakin' document.
sAndR.readFiles(fXmlFile, "\\", "/");
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder;
Document doc = null;
try {
dBuilder = dbFactory.newDocumentBuilder();
doc = dBuilder.parse(fXmlFile);
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SAXException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// optional, but recommended
// read this -
// http://stackoverflow.com/questions/13786607/normalization-in-dom-parsing-with-java-how-does-it-work
doc.getDocumentElement().normalize();
// Create an instance of an xpath object
XPath xPath = XPathFactory.newInstance().newXPath();
And then I go to town on it with various xpath queries that create the interface, yadda yadda.
My question is this; while this approach works, it seems freakishly weird to be creating and querying an actual file on the server rather than doing all this in a session object, but I can't find the correct way of doing this; what object/set of objects should I be using instead of this serialize-to-disk-and-read approach?
Thanks.
This question turned out to be so simple I'm considering deleting it just to prevent polluting stackoverflow; it was a basic misunderstanding of what Java could do with a String. I replaced all the file manipulation stuff with:
String fXmlFile = null;
if (fXmlFile != null) {} else {
File collectionTree = new File(bscConnector.GetCollection());
fXmlFile = collectionTree.toString();
fXmlFile = fXmlFile.replace("\\", "/");
}
and other than that left my code unchanged. All works, much faster too since it's not serializing and deserializing a large text file any more.
I'm going to move the initialization of the fXmlFile out of the JSP and into the servlet, define it as a session object, and pass it in as a part of the request because right now I'm having to declare it as null right before I test to see if it's null, which seems self-defeating. Other than that, it's all good.
Thanks eldjon.
Related
i'm trying to write the header for an xml file so it would be something like this:
<file xmlns="http://my_namespace"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://my_namespace file.xsd">
however, I can't seem to find how to do it using the Document class in java. This is what I have:
public void exportToXML() {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder;
try {
dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.newDocument();
doc.setXmlStandalone(true);
doc.createTextNode("<file xmlns=\"http://my_namespace"\n" +
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
"xsi:schemaLocation=\"http://my_namespace file.xsd\">");
Element mainRootElement = doc.createElement("MainRootElement");
doc.appendChild(mainRootElement);
for(int i = 0; i < tipoDadosParaExportar.length; i++) {
mainRootElement.appendChild(criarFilhos(doc, tipoDadosParaExportar[i]));
}
Transformer tr = TransformerFactory.newInstance().newTransformer();
tr.transform(new DOMSource(doc),
new StreamResult(new FileOutputStream(filename)));
} catch (Exception e) {
e.printStackTrace();
}
}
I tried writing it on the file using the createTextNode but it didn't work either, it only writes the version before showing the elements.
PrintStartXMLFile
Would appreciate if you could help me. Have a nice day
Your createTextNode() method is only suitable for creating text nodes, it's not suitable for creating elements. You need to use createElement() for this. If you're doing this by building a tree, then you need to build nodes, you can't write lexical markup.
I'm not sure what MainRootElement is supposed to be; you've only given a fragment of your desired output so it's hard to tell.
Creating a DOM tree and then serializing it is a pretty laborious way of constructing an XML file. Using something like an XMLEventWriter is easier. But to be honest, I got frustrated by all the existing approaches and wrote a new library for the purpose as part of Saxon 10. It's called simply "Push", and looks something like this:
Processor proc = new Processor();
Serializer serializer = proc.newSerializer(new File(fileName));
Push push = proc.newPush(serializer);
Document doc = push.document(true);
doc.setDefaultNamespace("http://my_namespace");
Element root = doc.element("root")
.attribute(new QName("xsi", "http://www.w3.org/2001/XMLSchema-instance", "schemaLocation"),
"http://my_namespace file.xsd");
doc.close();
Here's the XML I'm trying to parse: http://realtime.catabus.com/InfoPoint/rest/routes/get/51
#Override
protected Void doInBackground(String... Url) {
try {
URL url = new URL(Url[0]);
DocumentBuilderFactory dbf = DocumentBuilderFactory
.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
// Download the XML file
Document doc = db.parse(new InputSource(url.openStream()));
doc.getDocumentElement().normalize();
// Locate the Tag Name
nodelist = doc.getElementsByTagName("VehicleLocation");
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return null;
}
During runtime, when it reaches this line: DocumentBuilder db = dbf.newDocumentBuilder(); I get the following error:
Unexpected token (position:TEXT {"RouteId":51,"R...#1:1298 in java.io.InputStreamReader#850b9be)
It seems to have something to do with the encoding. My guess is that it's because the XML doesn't sepcify the encoding, but maybe not.
Is there a way to specify the encoding in the code (I can't change the XML itself)?
Thanks!
EDIT: This seems to only happen when parsing the XML from the url. Storing the file locally seems to work fine.
Is there a way to specify the encoding in the code (I can't change the
XML itself)?
You can call InputSource.setEncoding() to set the encoding.
I would suggest to take a look at XmlPullParser instead for parsing XML in Android.
I've been looking for the past few hours, and I can't find how to do it.
My XML file:
<list>
<Company id="01">
<Name>Atari</Name>
<Founded>1972</Founded>
<Consoles>
2600
5200
7800
</Consoles>
</Company>
<Company id="02">
<Name>Sega</Name>
<Founded>1960</Founded>
<Consoles>
Master System
Megadrive
Saturn
</Consoles>
</Company>
</list>
Basically, I want the code to find not only the name in one company block, but the name in any company block I wish, and be able to show it to other classes. So far, I've been able to show either or, but only by changing the code directly and not on the fly. The code I'm using:
private static String getTextValue(String def, Element doc, String tag) {
String value = def;
NodeList nl;
nl = doc.getElementsByTagName(tag);
if (nl.getLength() > 0 && nl.item(0).hasChildNodes()) {
value = nl.item(0).getFirstChild().getNodeValue();
}
if(value==null) value = " ";
return value;
}
public static boolean readXML(String xml) {
rolev = new ArrayList<String>();
Document dom;
// Make an instance of the DocumentBuilderFactory
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
try {
// use the factory to take an instance of the document builder
DocumentBuilder db = dbf.newDocumentBuilder();
// parse using the builder to get the DOM mapping of the
// XML file
dom = db.parse(xml);
Element doc = dom.getDocumentElement();
role1 = getTextValue(role1, doc, "Name");
if (getRole1() != null) {
if (!getRole1().isEmpty())
rolev.add(getRole1());
}
role2 = getTextValue(role2, doc, "Founded");
if (role2 != null) {
if (!role2.isEmpty())
rolev.add(role2);
}
role3 = getTextValue(role3, doc, "Consoles");
if (role3 != null) {
if (!role3.isEmpty())
rolev.add(role3);
}
role4 = getTextValue(role4, doc, "Name");
if ( role4 != null) {
if (!role4.isEmpty())
rolev.add(role4);
}
return true;
} catch (ParserConfigurationException pce) {
System.out.println(pce.getMessage());
} catch (SAXException se) {
System.out.println(se.getMessage());
} catch (IOException ioe) {
System.err.println(ioe.getMessage());
}
return false;
}
I used a lot of different methods, some searched up and some made on my own, but this one is the closest I can get to working for what I need. I need to be able to have the place it's looking change on the fly though.
You can use xpath to read XML. See Parsing XML with XPath in Java and/or How to read XML using XPath in Java
In your case, if you wanted to get all of the company names the set path you would use (basically the tag filter) would be "//list/Company/Name/text()"
The rest of the code can basically be copied from the questions I posted. Only your set (filter) would be different.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I have the following:
public static void main(String args[]) {
// upload config' data for program - param' are path and Xml's Root node/ where to get data from
confLoader conf = new confLoader("conf.xml", "config");
System.out.println(conf.getDbElement("dataSource") );
System.out.println(conf.getDbElement("dataSource") );
System.out.println(conf.getDbElement("dataSource") ); // Fails
...
The code that's responsible to build the DOM and parse from ('getDbElement()'):
public class confLoader{
DocumentBuilderFactory docBuilderFactory;
DocumentBuilder docBuilder;
Document doc;
NodeList nList;
public confLoader(String path, String XmlRoot){
try {
docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilder = docBuilderFactory.newDocumentBuilder();
doc = docBuilder.parse(new File(path));
// normalize text representation
doc.getDocumentElement().normalize();
nList = doc.getElementsByTagName(XmlRoot);
} catch (Exception e) {
e.printStackTrace();
}
}
public String getDbElement(String element) {
Node nNode = nList.item(0); // 1st item/node - sql
try {
if (nNode.getNodeType() == Node.ELEMENT_NODE) { ///// Line 36 - Problematic
Element eElement = (Element) nNode;
return (((Node) eElement.getElementsByTagName(element).item(0).getChildNodes().item(0)).getNodeValue());
}
} catch (Exception ex) {
System.out.println("Error retrieving " + element + " :" + ex.getMessage());//Thread.dumpStack();
ex.printStackTrace();
}
return "not available";
}
}
stacktrace for given code:
jdbc:mysql://localhost:...
java.lang.NullPointerException
jdbc:mysql://localhost:...
Error retrieving dataSource :null
not available
at exercise.confLoader.getDbElement(confLoader.java:36)
at exercise.Exercise.main(Exercise.java:22)
Line 36 : if (nNode.getNodeType() == Node.ELEMENT_NODE)
The xml parsing is done twice, and for the 3rd time I try to parse from Xml, I get the NullPointerException.
Too much code! Also, reading configuration pieces on demand is not that useful. And relying on instance variables makes your code more difficult to test and to understand, and even potentially unsafe in a concurrent scenario. You don't need all those classes, methods and things. It's just a matter of
public class Exercise {
public static void main(String[] args) throws XPathExpressionException {
XPath xpath = XPathFactory.newInstance().newXPath();
InputSource in = new InputSource("res/config.xml");
String user = xpath.evaluate("//sql/user/text()", in);
String password = xpath.evaluate("//sql/password/text()", in);
String path = xpath.evaluate("//sql/dataSource/text()", in);
Sql sql = new Sql(path, user, password);
}
}
You could optionally make your code a bit more complex, by storing all of your configuration in a Map<String, String>, but really you'd better use a common API like Properties, which is able to load from XML.
Problem solved by removing gnujaxp.jar from the build path.
First of all, I would recommend you don't chain too many methods on one line. Breaking the call structure up into multiple lines will increase readability and ease debugging.
For exaple, rewrite:
return (((Node) (eElement.getElementsByTagName("password").item(0).getChildNodes().item(0)).getNodeValue());
to:
NodeList rootEls = eElement.getElementsByTagName("password");
Node rootEl = rootEls.item(0)
NodeList children = rootEl.getChildNodes();
Node passEl = children.item(0);
return passEl.getNodeValue();
When you get the NullPointerException, with this code, you can extract a lot more information from the line number in the exception.
Secondly, in this case it may prove useful to take a look at the various XML processing libraries for Java and to find one which allows the use of XPath, see also: this tutorial on Xpath.
I hope this helps. Feel free to pose any questions.
I try to transform XML document using XSLT. As an input I have www.wordpress.org XHTML source code, and XSLT is dummy example retrieving site's title (actually it could do nothing - it doesn't change anything).
Every single API or library I use, transformation takes about 2 minutes! If you take a look at wordpress.org source, you will notice that it is only 183 lines of code. As I googled it is probably due to DOM tree building. No matter how simple XSLT is, it is always 2 minutes - so it confirms idea that it's related to DOM building, but anyway it should not take 2 minutes in my opinion.
Here is an example code (nothing special):
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = null;
try {
transformer = tFactory.newTransformer(
new StreamSource("/home/pd/XSLT/transf.xslt"));
} catch (TransformerConfigurationException e) {
e.printStackTrace();
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
System.out.println("START");
try {
transformer.transform(new SAXSource(new InputSource(
new FileInputStream("/home/pd/XSLT/wordpress.xml"))),
new StreamResult(outputStream));
} catch (TransformerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("STOP");
System.out.println(new String(outputStream.toByteArray()));
It's between START and STOP where java "pauses" for 2 minutes. If I take a look at the processor or memory usage, nothing increases. It looks like really JVM stopped...
Do you have any experience in transforming XMLs that are longer than 50 (this is random number ;)) lines? As I read XSLT always needs to build DOM tree in order to do its work. Fast transformation is crucial for me.
Thanks in advance,
Piotr
Does the sample HTML file use namespaces? If so, your XML parser may be attempting to retrieve contents (a schema, perhaps) from the namespace URIs. This is likely if each run takes exactly two minutes -- it's likely one or more TCP timeouts.
You can verify this by timing how long it takes to instantiate your InputSource object (where the WordPress XML is actually parsed), as this is likely the line which is causing the delay. After reviewing the sample file you posted, it does include a declared namespace (xmlns="http://www.w3.org/1999/xhtml").
To work around this, you can implement your own EntityResolver which essentially disables the URL-based resolution. You may need to use a DOM -- see DocumentBuilder's setEntityResolver method.
Here's a sample using DOM and disabling resolution (note -- this is untested):
try {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbFactory.newDocumentBuilder();
db.setEntityResolver(new EntityResolver() {
#Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
return null; // Never resolve any IDs
}
});
System.out.println("BUILDING DOM");
Document doc = db.parse(new FileInputStream("/home/pd/XSLT/wordpress.xml"));
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(
new StreamSource("/home/pd/XSLT/transf.xslt"));
System.out.println("RUNNING TRANSFORM");
transformer.transform(
new DOMSource(doc.getDocumentElement()),
new StreamResult(outputStream));
System.out.println("TRANSFORMED CONTENTS BELOW");
System.out.println(outputStream.toString());
} catch (Exception e) {
e.printStackTrace();
}
If you want to use SAX, you would have to use a SAXSource with an XMLReader which uses your custom resolver.
The commenters who've posted that the answer likely resides with the EntityResolver are probably correct. However, the solution may not be to simply not load the schemas but rather load them from the local file system.
So you could do something like this
db.setEntityResolver(new EntityResolver() {
#Override
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
try {
FileInputStream fis = new FileInputStream(new File("classpath:xsd/" + systemId));
InputSource is = new InputSource(fis);
return is
} catch (FileNotFoundException ex) {
logger.error("File Not found", ex);
return null;
}
}
});
Chances are the problem isn't with the call transfomer.transform. It's more likely that you are doing something in your xslt that is taking forever. My suggestion would be use a tool like Oxygen or XML Spy to profile your XSLT and find out which templates are taking the longest to execute. Once you've determined this you can begin to optimize the template.
If you are debugging your code on an android device, make sure you try it without eclipse attached to the process. When I was debugging my app xslt transformations were taking 8 seconds, where the same process took a tenth of a second on ios in native code. Once I ran the code without eclipse attached to it, the process took a comparable amount of time to the c based counterpart.