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.
Related
I have this response string as XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns:SRVResponse>
<ns:Response>
<ns1:ServiceHeader>
<ns1:rsHeader>
<ns1:status>
<ns1:finStatus>E</ns1:finStatus>
</ns1:status>
</ns1:rsHeader>
</ns1:ServiceHeader>
</ns:Response>
</ns:SRVResponse>
</soapenv:Body>
</soapenv:Envelope>
Im trying to fetch this finStatus tag value. This comes as part of ns1, and some times it comes as ns2. So, I dont want to depend on this. I just need to fetch if the tag has finStatus tag.
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(strResponse));
if(is != null) {
Document doc = db.parse(is);
NodeList idDetails = doc.getDocumentElement().getElementsByTagNameNS("*", "status");
if(idDetails != null) {
int length = idDetails.getLength();
for (int i = 0; i < length; i++) {
if (idDetails.item(i).getNodeType() == Node.ELEMENT_NODE) {
Element el = (Element) idDetails.item(i);
if (el.getNodeName().contains("status")) {
status = getElementTextContent(el, "ns1:finStatus");
System.out
.println("Status :"+status);
}
}
}
}
}
Method
public static String getElementTextContent(Element el, String elemTag) {
String result = "";
if(el.getElementsByTagName(elemTag) != null) {
if(el.getElementsByTagName(elemTag).item(0) != null) {
if(el.getElementsByTagName(elemTag).item(0).getTextContent() != null) {
result = el.getElementsByTagName(elemTag).item(0).getTextContent();
} else {
result = "";
}
}
}
return result;
}
This is working only because Im passing the tag as ns1:finStatus,
How can I achieve this , not to based on namespace tag.
1) You are already fetching node without namespace using. So I do not see problem.
2) Use the * search in your second method as well i.e in your getElementTextContent(), use el.getElementsByTagNameNS("*",elemTag).
3) To skip the ns: prefix before passing it to getElementTextContent, Use getLocalName() instead of getNodeName()
Install an XPath 2.0 library and do //*:finStatus. Doing this by DOM navigation is just masochism. Either that, or you're being paid for the number of lines of code you write.
I am parsing XML from lots of JMS messaging topics, so the structure of each message varies a lot and I'd like to make one general tool to parse them all.
To start, all I want to do is get the element names:
<gui-action>
<action>some action</action>
<params>
<param1>blue</param1>
<param2>tall</param2>
<params>
</gui-action>
I just want to retrieve the strings "gui-action", "action", "params", "param1", and "param2." Duplicates are just fine.
I've tried using org.w3c.dom.Node, Element, NodeLists and I'm not having much luck. I keep getting the element values, not the names.
private Element root;
private Document doc;
private NodeList nl;
//messageStr is passed in elsewhere in the code
//but is a string of the full XML message.
doc = xmlParse( messageStr );
root = doc.getDocumentElement();
nl = root.getChildNodes();
int size = nl.getLength();
for (int i=0; i<size; i++) {
log.info( nl.item(i).getNodeName() );
}
public Document xmlParse( String xml ){
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
InputSource is;
try {
//Using factory get an instance of document builder
db = dbf.newDocumentBuilder();
is = new InputSource(new StringReader( xml ) );
doc = db.parse( is );
} catch(ParserConfigurationException pce) {
pce.printStackTrace();
} catch(SAXException se) {
se.printStackTrace();
} catch(IOException ioe) {
ioe.printStackTrace();
}
return doc;
//parse using builder to get DOM representation of the XML file
}
My logged "parsed" XML looks like this:
#text
action
#text
params
#text
Figured it out. I was iterating over only the child nodes, and not including the parent. So now I just filter out the #texts, and include the parent. Derp.
log.info(root.getNodeName() );
for (int i=0; i<size; i++) {
nodeName = nl.item(i).getNodeName();
if( nodeName != "#text" ) {
log.info( nodeName );
}
}
Now if anyone knows a way to get a NodeList of the entire document, that would be awesome.
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.
I use Wikimedia API Sandbox for Japanese.
Japanese Version
English Version
I send a HTTP request to Wikimedia and I get a result formed in XML.
When I try to send a request and get a result on API Sandbox Webpage, there is no character corruption in a result.
But when I get a result in Java, a result includes character corruptions.
I cannot assign a specific character code in XML file.
How can I assign a result a specific character code?
How can I resolve my problem?
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db
.parse(new URL(
"http://ja.wikipedia.org/w/api.php?action=query&prop=categories&format=xml&cllimit=10&titles="
+ key).openStream());
Element root = doc.getDocumentElement();
NodeList queryList = root.getChildNodes();
Node query = queryList.item(0);
if (query instanceof Element) {
Element queryEle = (Element) query;
NodeList pagesList = queryEle.getChildNodes();
Node pgs = pagesList.item(0);
if (pgs instanceof Element) {
Element pagesElement = (Element) pgs;
NodeList pageList = pagesElement.getChildNodes();
Node page = pageList.item(0);
if (page instanceof Element) {
Element pageElement = (Element) page;
String title = pageElement.getAttribute("title");
title = new String(title.getBytes("UTF-8"), "UTF-8");
}
}
}
} catch (ParserConfigurationException e) {
} catch (SAXException e) {
} catch (IOException e) {
}
Now I send a request, I got a result whose page title is "大学". But in Java, it shows "??".
I use above code for Android Application.
title = new String(title.getBytes("UTF-8"), "UTF-8"); can be left out.
It worked for me, for key=1 (receiving UTF-8). I have a UTF-8 Linux PC though. Maybe you did not output in a UTF-8 context or so. Try write the Document to a file.
You could do more inspection:
URLConnection connection = new URL("...").openConnection();
... connection.getContentEncoding();
... connection.getContentType();
InputStream in = connection.openStream();
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.