Tuesday, January 27, 2009

Java Properties in XML

Java properties have been a staple of Java development for many years. Even today, Java properties are used in popular frameworks and tools such as the Spring Framework and Ant. Most of the Java properties that I have seen used frequently follow the tried-and-true name=value paradigm. However, since J2SE 5, it has been easy to load (and save) properties in XML format.

In my experience, the typical properties file looks something like that shown next.

examples.properties


url.blog.dustin=http://marxsoftware.blogspot.com/
url.javaworld=http://www.javaworld.com/
url.coloradosoftwaresummit=http://www.softwaresummit.com/
url.otn=http://www.oracle.com/technology/index.html
url.rmoug=http://www.rmoug.org/


J2SE 5 made it easy to load properties from XML (and store properties to XML). The Javadoc-based API documentation for the Properties class discusses both formats. This documentation shows the DTD used to define the Properties XML grammar:


<?xml version="1.0" encoding="UTF-8"?>
<!-- DTD for properties -->
<!ELEMENT properties ( comment?, entry* ) >
<!ATTLIST properties version CDATA #FIXED "1.0">
<!ELEMENT comment (#PCDATA) >
<!ELEMENT entry (#PCDATA) >
<!ATTLIST entry key CDATA #REQUIRED>


The DTD shows us that properties stored in XML must have <properties> as the root element required of well-formed XML and can have zero or one <comment> elements nested in this root tag. We also learn from this DTD that zero to many elements name <entry> are allowed and that an entry element may contain a data body and a single attribute named key. Based on this DTD, we could write a compatible XML-based properties file by hand, but an even easier way to see one is to read in a traditional properties file of name/value pairs and store it back out in XML format. This is exactly what the next Java class, PropertiesExamples, does.

PropertiesExamples.java


package dustin.properties;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;

public class PropertiesExamples
{
/** No-arguments constructor. */
public PropertiesExamples() {}

/**
* Get traditional properties in name=value format.
*
* @param filePathAndName Path and name of properties file (without the
* .properties extension).
* @return Properties read in from provided file.
*/
public Properties loadTraditionalProperties(
final String filePathAndName)
{
final Properties properties = new Properties();
try
{
final FileInputStream in = new FileInputStream(filePathAndName);
properties.load(in);
in.close();
}
catch (FileNotFoundException fnfEx)
{
System.err.println("Could not read properties from file " + filePathAndName);
}
catch (IOException ioEx)
{
System.err.println(
"IOException encountered while reading from " + filePathAndName);
}
return properties;
}

/**
* Store provided properties in XML format.
*
* @param sourceProperties Properties to be stored in XML format.
* @param out OutputStream to which to write XML formatted properties.
*/
public void storeXmlProperties(
final Properties sourceProperties,
final OutputStream out)
{
try
{
sourceProperties.storeToXML(out, "This is easy!");
}
catch (IOException ioEx)
{
System.err.println("ERROR trying to store properties in XML!");
}
}

/**
* Store provided properties in XML format to provided file.
*
* @param sourceProperties Properties to be stored in XML format.
* @param pathAndFileName Path and name of file to which XML-formatted
* properties will be written.
*/
public void storeXmlPropertiesToFile(
final Properties sourceProperties,
final String pathAndFileName)
{
try
{
FileOutputStream fos = new FileOutputStream(pathAndFileName);
storeXmlProperties(sourceProperties, fos);
fos.close();
}
catch (FileNotFoundException fnfEx)
{
System.err.println("ERROR writing to " + pathAndFileName);
}
catch (IOException ioEx)
{
System.err.println(
"ERROR trying to write XML properties to file " + pathAndFileName);
}
}

/**
* Runs main examples.
*
* @param arguments Command-line arguments; none anticipated.
*/
public static void main(final String[] arguments)
{
final PropertiesExamples me = new PropertiesExamples();
final Properties inputProperties =
me.loadTraditionalProperties("examples.properties");
me.storeXmlPropertiesToFile(inputProperties, "examples-xml.properties");
}
}


The class shown above reads in the properties file listed earlier and then writes it back out in XML format. The actual lines of code doing most of the work are small in number, but the many checked exceptions associated with file input/output make the code base much larger.

When this code is run, the following output is generated:

examples-xml.properties


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>This is easy!</comment>
<entry key="url.coloradosoftwaresummit">http://www.softwaresummit.com/</entry>
<entry key="url.rmoug">http://www.rmoug.org/</entry>
<entry key="url.blog.dustin">http://marxsoftware.blogspot.com/</entry>
<entry key="url.javaworld">http://www.javaworld.com/</entry>
<entry key="url.otn">http://www.oracle.com/technology/index.html</entry>
</properties>


This generated XML file contains the same name/value pairs as the traditional properties file shown earlier, can be read in like the traditional version using the Properties.loadFromXML, and includes the comment that was passed to the Properties.storeToXML method.

Conclusion

It is fairly straightforward to load properties from XML and to store them as XML. However, the XML is essentially limited to the same paradigm of name/value pairs as traditional properties files. Therefore, we are unable to take advantage of XML's hierarchical nature to use relationships more complex than one key (name) to one value. The primary reason one might use Java's support for XML-based properties is if XML was being used for other tools or frameworks and the properties in XML were more accessible to the other tool or framework.

4 comments:

Fernando said...

Hello there!

First of all, nice article, despite the fact I can not see any advantage of that xml syntax over the properties files one.

For all those who wish to store properties in xml files and can not use j2se 5, they can use XStream, a very simple xml serializer, which can serialize/deserialize Properties (among many others) from very simple xml files.

I have been using it for years (for my config files, AJAX and anything that would require simple and not-too-long .xml files) and I am very happy with it.

Best regards.

Unknown said...

Like Fernando, I like the post but I can see little use in this. However, it is not impossible to imagine someone entering the format incorrectly in a properties file (the only example I can think of would be if someone forgot the '=' between the key and value).

It would be more difficult for this to happen if the property file was saved as XML (both from a programmer error standpoint and an XML validation standpoint)

pete said...

Nile, Fernando...

One advantage could be in forwarding an XML to a service that expects XML input, e.g. XSL. Java has an inbuilt Properties parser to interpret name=value text files. Not all non-Java environments will; most will support XML.

Μπάμπης said...

The big advantage is that when the properties file contains localized data (for example UI labels), you don't have to encode the file if xml is used.