Return-Path: Delivered-To: apmail-xml-commons-dev-archive@www.apache.org Received: (qmail 77375 invoked from network); 1 Jun 2009 15:52:22 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 1 Jun 2009 15:52:22 -0000 Received: (qmail 88019 invoked by uid 500); 1 Jun 2009 15:44:01 -0000 Delivered-To: apmail-xml-commons-dev-archive@xml.apache.org Received: (qmail 87996 invoked by uid 500); 1 Jun 2009 15:44:00 -0000 Mailing-List: contact commons-dev-help@xml.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list commons-dev@xml.apache.org Received: (qmail 87948 invoked by uid 99); 1 Jun 2009 15:44:00 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 01 Jun 2009 15:44:00 +0000 X-ASF-Spam-Status: No, hits=1.1 required=10.0 tests=FB_GET_MEDS,SPF_HELO_PASS,SPF_PASS X-Spam-Check-By: apache.org Received-SPF: pass (nike.apache.org: domain of hoju@visi.com designates 208.42.176.221 as permitted sender) Received: from [208.42.176.221] (HELO g2host.com) (208.42.176.221) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 01 Jun 2009 15:43:49 +0000 Received: from [162.136.193.1] (account hoju@visi.com) by mailback2.g2host.com (CommuniGate Pro WEBUSER 5.1.16) with HTTP id 14788676 for commons-dev@xml.apache.org; Mon, 01 Jun 2009 10:43:27 -0500 From: "Jacob Kjome" Subject: Re: Proposal - A Very Simple API for Reading Simple XML Data To: commons-dev@xml.apache.org X-Mailer: CommuniGate Pro WebUser v5.1.16 Date: Mon, 01 Jun 2009 10:43:27 -0500 Message-ID: In-Reply-To: <23804602.post@talk.nabble.com> References: <23804602.post@talk.nabble.com> MIME-Version: 1.0 Content-Type: text/plain;charset=iso-8859-1;format="flowed" Content-Transfer-Encoding: 8bit X-Virus-Checked: Checked by ClamAV on apache.org Have you considered commons-configuration [1]?� It seems to me it does what you want, while being very flexible and robust. [1] http://commons.apache.org/configuration/ Jake On Sun, 31 May 2009 09:24:48 -0700 (PDT) chris0 wrote: > > Hello, > > I'm making this proposal because I couldn't find a good solution to a > problem that I recently had. > > What I wanted to do was to configure an application with a simple XML file, > something along the lines of: > > > test > major="1" > minor="2"/> > > > > > > > > > > > So the point is that it's really a very simple bit of XML data and what I > wanted was a nice easy way to read it in. The current options seem to be > SAX, DOM or JAXB. > > To use JAXB I need an XSD which is just complicating things too much for a > simple config file. SAX is a bit low-level and complicated too. The best > option is probably DOM, but even DOM is quite verbose. Infact DOM is verbose > enough that it made me think for a while if I could just use some other > mechanism such as a properties file so that I didn't have to bother with > DOM. > >For the purpose of this proposal I've written what I think is pretty much > the most concise way to parse this data with DOM. Also note that I want to > provide some basic error reporting if the XML data is not in the expected > format. The code follows below: > > FileInputStream fileInputStream = null; > try > { > String rootName = "config"; > fileInputStream = new FileInputStream("config.xml"); > DocumentBuilderFactory builderFactory = > DocumentBuilderFactory.newInstance(); > DocumentBuilder builder = builderFactory.newDocumentBuilder(); > Document document = builder.parse(fileInputStream); > Element rootElement = document.getDocumentElement(); > if(!rootElement.getNodeName().equals(rootName)) > throw new Exception("Could not find root node: "+rootName); > > NodeList titles = rootElement.getElementsByTagName("title"); > if(titles.getLength()!=1) throw new Exception("Could not find individual > node: title"); > Node title = titles.item(0); > out.println("title: "+title.getTextContent()); > > NodeList versions = rootElement.getElementsByTagName("version"); > if(versions.getLength()!=1) throw new Exception("Could not find individual > node: version"); > Node version = versions.item(0); > NamedNodeMap versionAttributes = version.getAttributes(); > Node major = versionAttributes.getNamedItem("major"); > if(major==null) throw new Exception("Could not find attribute: major"); > Node minor = versionAttributes.getNamedItem("minor"); > if(minor==null) throw new Exception("Could not find attribute: minor"); > out.println( > "version: > "+Integer.parseInt(major.getNodeValue())+"."+Integer.parseInt(minor.getNodeValue())); > > NodeList roles = rootElement.getElementsByTagName("roles"); > if(roles.getLength()!=1) throw new Exception("Could not find individual > node: roles"); > > NodeList roleList = ((Element)roles.item(0)).getElementsByTagName("role"); > int n = roleList.getLength(); > for(int i=0;i { > Node role = roleList.item(i); > Node roleName = role.getAttributes().getNamedItem("name"); > if(roleName==null) throw new Exception("Could not find attribute: name"); > out.println("role: name: "+roleName.getNodeValue()); > } > > NodeList users = rootElement.getElementsByTagName("users"); > if(users.getLength()!=1) throw new Exception("Could not find individual > node: users"); > > NodeList userList = ((Element)users.item(0)).getElementsByTagName("user"); > n = userList.getLength(); > for(int i=0;i { > Node user = userList.item(i); > NamedNodeMap userAttributes = user.getAttributes(); > Node userName = userAttributes.getNamedItem("name"); > if(userName==null) throw new Exception("Could not find attribute: name"); > Node userPassword = userAttributes.getNamedItem("password"); > if(userPassword==null) throw new Exception("Could not find attribute: > password"); > Node userRole = userAttributes.getNamedItem("role"); > if(userPassword==null) throw new Exception("Could not find attribute: > role"); > out.println( > "user: name: "+userName.getNodeValue()+ > ", password: "+userPassword.getNodeValue()+ > ", role: "+userRole.getNodeValue()); > } > } > finally > { > if(fileInputStream!=null) fileInputStream.close(); > } > > Output: > > title: test > version: 1.2 > role: name: admin > role: name: user > user: name: joe, password: pass, role: admin > user: name: harry, password: secret, role: user > > And it's pretty verbose, 65 lines in all. > > What I then decided to do was to write a very simple utility class called > XmlData that could be used to get this information as easily as possible, > and that would have basic built-in error reporting. When using the XmlData > class it is possible to write equivalent code to the above in a fraction of > the number of lines: > > XmlData config = new XmlData("config.xml","config"); > > out.println("title: "+config.child("title").content()); > > XmlData version = config.child("version"); > out.println("version: > "+version.integer("major")+"."+version.integer("minor")); > > for(XmlData role:config.child("roles").children("role")) out.println("role: > name: "+role.string("name")); > > for(XmlData user:config.child("users").children("user")) > { > out.println( > "user: name: "+user.string("name")+ > ", password: "+user.string("password")+ > ", role: "+user.string("role")); > } > > Output: > > title: test > version: 1.2 > role: name: admin > role: name: user > user: name: joe, password: pass, role: admin > user: name: harry, password: secret, role: user > > As you can see, this is a really simple way to read basic XML files. All > nodes, attributes and content can very easily be accessed, and only one > class is required. > > The XmlData class uses DOM to read an XML file then builds its own > representation of the tree in a very simple form, with only the basic data > present and all data exposed using standard Collections classes where > appropriate. The XmlData class source is included at the end of this post. > > So in summary, the main point of this single utility class, XmlData, is to > provide a very simple and user-friendly way of reading simple XML files that > may contain simple data such as startup configurations. This class is not > supposed to be a substitute for DOM and is not intended for high-performance > scenarios, rather to make XML as easily accessible as possible. > > [One improvement to this may be to base XmlData on SAX rather than DOM in > order to increase performance] > > My proposal is perhaps to see something like XmlData as a Commons utility > class. > > All comments very welcome. > > Thanks, > > Chris. > > -- > > package main; > > import java.io.FileInputStream; > import java.util.ArrayList; > import java.util.Collections; > import java.util.HashMap; > import java.util.List; > import java.util.Map; > > import javax.xml.parsers.DocumentBuilder; > import javax.xml.parsers.DocumentBuilderFactory; > > import org.w3c.dom.Document; > import org.w3c.dom.Element; > import org.w3c.dom.NamedNodeMap; > import org.w3c.dom.Node; > import org.w3c.dom.NodeList; > > public class XmlData > { > private static Element rootElement(String filename, String rootName) throws > Exception > { > FileInputStream fileInputStream = null; > try > { > fileInputStream = new FileInputStream(filename); > DocumentBuilderFactory builderFactory = > DocumentBuilderFactory.newInstance(); > DocumentBuilder builder = builderFactory.newDocumentBuilder(); > Document document = builder.parse(fileInputStream); > Element rootElement = document.getDocumentElement(); > if(!rootElement.getNodeName().equals(rootName)) > throw new RuntimeException("Could not find root node: "+rootName); > return rootElement; > } > finally > { > if(fileInputStream!=null) fileInputStream.close(); > } > } > > public XmlData(String filename, String rootName) throws Exception > { > this(rootElement(filename,rootName)); > } > > private XmlData(Element element) > { > this.name = element.getNodeName(); > this.content = element.getTextContent(); > NamedNodeMap namedNodeMap = element.getAttributes(); > int n = namedNodeMap.getLength(); > for(int i=0;i { > Node node = namedNodeMap.item(i); > String name = node.getNodeName(); > addAttribute(name,node.getNodeValue()); > } > NodeList nodes = element.getChildNodes(); > n = nodes.getLength(); > for(int i=0;i { > Node node = nodes.item(i); > int type = node.getNodeType(); > if(type==Node.ELEMENT_NODE) addChild(node.getNodeName(),new > XmlData((Element)node)); > } > } > > private void addAttribute(String name, String value) > { > nameAttributes.put(name,value); > } > > private void addChild(String name, XmlData child) > { > List children = nameChildren.get(name); > if(children==null) > { > children = new ArrayList(); > nameChildren.put(name,children); > } > children.add(child); > } > > public String name() > { > return name; > } > > public String content() > { > return content; > } > > public XmlData child(String name) throws Exception > { > List children = children(name); > if(children.size()!=1) throw new Exception("Could not find individual > child node: "+name); > return children.get(0); > } > > public List children(String name) > { > List children = nameChildren.get(name); > return children==null ? Collections.EMPTY_LIST : children; > } > > public String string(String name) throws Exception > { > String value = nameAttributes.get(name); > if(value==null) throw new Exception("Could not find attribute: "+name+", > in node: "+this.name); > return value; > } > > public int integer(String name) throws Exception > { > return Integer.parseInt(string(name)); > } > > private String name; > private String content; > private Map nameAttributes = new HashMap(); > private Map> nameChildren = new > HashMap>(); > } > -- > View this message in context: >http://www.nabble.com/Proposal---A-Very-Simple-API-for-Reading-Simple-XML-Data-tp23804602p23804602.html > Sent from the Apache XML - Commons - Dev mailing list archive at Nabble.com. > >