xml-commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Jacob Kjome" <h...@visi.com>
Subject Re: Proposal - A Very Simple API for Reading Simple XML Data
Date Mon, 01 Jun 2009 15:43:27 GMT
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 <technical0@gmail.com> 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:
> 
> <config>
> 	<title>test</title>
> 	<version
> 		major="1"
> 		minor="2"/>
> 	<roles>
> 		<role name="admin"/>
> 		<role name="user"/>
> 	</roles>
> 	<users>
> 		<user name="joe" password="pass" role="admin"/>
> 		<user name="harry" password="secret" role="user"/>
> 	</users>
> </config>
> 
> 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<n;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<n;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<n;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<n;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<XmlData> children = nameChildren.get(name);
> 		if(children==null)
> 		{
> 			children = new ArrayList<XmlData>();
> 			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<XmlData> children = children(name);
> 		if(children.size()!=1) throw new Exception("Could not find individual
> child node: "+name);
> 		return children.get(0);
> 	}
> 	
> 	public List<XmlData> children(String name)
> 	{
> 		List<XmlData> 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<String,String> nameAttributes = new HashMap<String,String>();
> 	private Map<String,List<XmlData>> nameChildren = new
> HashMap<String,List<XmlData>>();
> }
> -- 
> 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.
> 
> 


Mime
View raw message