Get XML only immediate children elements by name
I realise you found something of a solution to this in May @kentcdodds but I just had a fairly similar problem which I've now found, I think (perhaps in my usecase, but not in yours), a solution to.
a very simplistic example of my XML format is shown below:-
<?xml version="1.0" encoding="utf-8"?><rels> <relationship num="1"> <relationship num="2"> <relationship num="2.1"/> <relationship num="2.2"/> </relationship> </relationship> <relationship num="1.1"/> <relationship num="1.2"/></rels>
As you can hopefully see from this snippet, the format I want can have N-levels of nesting for [relationship] nodes, so obviously the problem I had with Node.getChildNodes() was that I was getting all nodes from all levels of the hierarchy, and without any sort of hint as to Node depth.
Looking at the API for a while , I noticed there are actually two other methods that might be of some use:-
Together, these two methods seemed to offer everything that was required to get all of the immediate descendant elements of a Node. The following jsp code should give a fairly basic idea of how to implement this. Sorry for the JSP. I'm rolling this into a bean now but didn't have time to create a fully working version from picked apart code.
<%@page import="javax.xml.parsers.DocumentBuilderFactory, javax.xml.parsers.DocumentBuilder, org.w3c.dom.Document, org.w3c.dom.NodeList, org.w3c.dom.Node, org.w3c.dom.Element, java.io.File" %><% try { File fXmlFile = new File(application.getRealPath("/") + "/utils/forms-testbench/dom-test/test.xml"); DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); Document doc = dBuilder.parse(fXmlFile); doc.getDocumentElement().normalize(); Element docEl = doc.getDocumentElement(); Node childNode = docEl.getFirstChild(); while( childNode.getNextSibling()!=null ){ childNode = childNode.getNextSibling(); if (childNode.getNodeType() == Node.ELEMENT_NODE) { Element childElement = (Element) childNode; out.println("NODE num:-" + childElement.getAttribute("num") + "<br/>\n" ); } }} catch (Exception e) { out.println("ERROR:- " + e.toString() + "<br/>\n");}%>
This code would give the following output, showing only direct child elements of the initial root node.
NODE num:-1NODE num:-1.1NODE num:-1.2
Hope this helps someone anyway. Cheers for the initial post.
You can use XPath for this, using two path to get them and process them differently.
To get the <file>
nodes direct children of <notification>
use //notification/file
and for the ones in <group>
use //groups/group/file
.
This is a simple sample:
public class SO10689900 { public static void main(String[] args) throws Exception { DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); Document doc = db.parse(new InputSource(new StringReader("<notifications>\n" + " <notification>\n" + " <groups>\n" + " <group name=\"zip-group.zip\" zip=\"true\">\n" + " <file location=\"C:\\valid\\directory\\\" />\n" + " <file location=\"C:\\this\\file\\doesn't\\exist.grr\" />\n" + " <file location=\"C:\\valid\\file\\here.txt\" />\n" + " </group>\n" + " </groups>\n" + " <file location=\"C:\\valid\\file.txt\" />\n" + " <file location=\"C:\\valid\\file.xml\" />\n" + " <file location=\"C:\\valid\\file.doc\" />\n" + " </notification>\n" + "</notifications>"))); XPath xpath = XPathFactory.newInstance().newXPath(); XPathExpression expr1 = xpath.compile("//notification/file"); NodeList nodes = (NodeList)expr1.evaluate(doc, XPathConstants.NODESET); System.out.println("Files in //notification"); printFiles(nodes); XPathExpression expr2 = xpath.compile("//groups/group/file"); NodeList nodes2 = (NodeList)expr2.evaluate(doc, XPathConstants.NODESET); System.out.println("Files in //groups/group"); printFiles(nodes2); } public static void printFiles(NodeList nodes) { for (int i = 0; i < nodes.getLength(); ++i) { Node file = nodes.item(i); System.out.println(file.getAttributes().getNamedItem("location")); } }}
It should output:
Files in //notificationlocation="C:\valid\file.txt"location="C:\valid\file.xml"location="C:\valid\file.doc"Files in //groups/grouplocation="C:\valid\directory\"location="C:\this\file\doesn't\exist.grr"location="C:\valid\file\here.txt"
Well, the DOM solution to this question is actually pretty simple, even if it's not too elegant.
When I iterate through the filesNodeList
, which is returned when I call notificationElement.getElementsByTagName("file")
, I just check whether the parent node's name is "notification". If it isn't then I ignore it because that will be handled by the <group>
element. Here's my code solution:
for (int j = 0; j < filesNodeList.getLength(); j++) { Element fileElement = (Element) filesNodeList.item(j); if (!fileElement.getParentNode().getNodeName().equals("notification")) { continue; } ...}