I have a configuration file in XML, which looks something like:
<configuration>
<database>
<host></host>
<port></port>
</database>
<queue>
<host></host>
<port></port>
<type></type>
</queue>
</configuration>
I would like to use JAXB/xjc to generate Java classes for this configuration, however I would like to generate these classes and unmarshal these one level into the tree. Rather than receiving a Configuration.java, I would like a Database.java and Queue.java (in order that these can be separately injected into a Guice managed application). I don't (currently) see any way of doing this, however may be searching for the wrong things.
After some experimentation, I have found a solution to generate these classes and be able to populate and return these based on class:
First, I added a bindings.xjb file which will generate the contained classes (Database and Queue in this example)
<jaxb:bindings
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
version="2.1">
<jaxb:globalBindings localScoping="toplevel"/>
</jaxb:bindings>
However, JAXB can't unmarshall using the Database or Queue class, only the Configuration class (this is where I may have missed something). I can do
JAXBContext context = JAXBContext.newInstance(Configuration.class);
Unmarshaller um = context.createUnmarshaller();
Configuration conf = (Configuration) um.unmarhal(xmlFile);
but not
JAXBContext context = JAXBContext.newInstance(Database.class);
Unmarshaller um = context.createUnmarshaller();
Database db = (Database) um.unmarhal(xmlFile);
However, because I can get the database object by calling getDatabase() on an instance of the Configuration object, this can also be made generic using reflection (making this code cache results in the appropriate places is another exercise):
T item = null;
try {
JAXBContext context = JAXBContext.newInstance(Configuration.class);
Unmarshaller um = context.createUnmarshaller();
Configuration conf = (Configuration) um.unmarshal(xmlFile);
Method[] allMethods = Configuration.class.getDeclaredMethods();
for (Method method : allMethods)
{
if (method.getReturnType().equals(clazz))
{
item = (T) method.invoke(conf);
break;
}
}
} catch (JAXBException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new ConfigException("Failure detected while loading configuration", e);
}
return item;
I'm not sure this is the best solution (I only started working with JAXB yesterday), but seems to fulfill my goal.