In a configuration file I have multiple array values, for example names for a phonebook.
<configuration>
<phonebook>
<name>John</name>
<name>Mary</name>
</phonebook>
</configuration>
I want to read these into this bean via BeanHelper (or another way):
public interface Phonebook {
public void setName(String[] names);
public String[] getName();
}
Or ideally with lists java.util.List<String>
instead of arrays String[]
.
The implementation of the interface is simple:
public class MyPhonebook implements Phonebook {
private String[] names;
@Override
public void setName(String[] names) {
this.names = names;
}
@Override
public String[] getName() {
return names;
}
@Override
public String toString() {
return String.join("; ", names);
}
}
The main looks like this:
import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.ConfigurationRuntimeException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.configuration.beanutils.BeanHelper;
import org.apache.commons.configuration.beanutils.XMLBeanDeclaration;
public class Main {
private static Phonebook myPhonebook;
private static XMLConfiguration config;
public static void main(String[] args) {
// read the configuration
String configFile = "config.xml";
try {
config = new XMLConfiguration();
// config.setAttributeSplittingDisabled(true);
config.setFileName(configFile);
config.load();
} catch (ConfigurationException e) {
e.printStackTrace();
}
// this is to see which keys and values are inside the configuration
config.getKeys().forEachRemaining(
s -> System.out.println(s + " : " + config.getProperty(s)));
// try to create the bean
try {
XMLBeanDeclaration declaration = new XMLBeanDeclaration(config,
"phonebook");
declaration.getBeanProperties().forEach(
(k, v) -> System.out.println(k + " : " + v));
myPhonebook = (Phonebook) BeanHelper.createBean(declaration,
MyPhonebook.class);
} catch (ConfigurationRuntimeException e) {
e.printStackTrace();
}
// print out the bean
System.out.println(myPhonebook);
}
}
This does not work when trying to create the bean, I get a ConfigurationRuntimeException
caused by this UnsupportedOperationException
:
java.lang.UnsupportedOperationException: Unable to handle collection of type : [Ljava.lang.String; for property name
at org.apache.commons.configuration.beanutils.BeanHelper.createPropertyCollection(BeanHelper.java:335)
at org.apache.commons.configuration.beanutils.BeanHelper.initBean(BeanHelper.java:213)
at org.apache.commons.configuration.beanutils.DefaultBeanFactory.initBeanInstance(DefaultBeanFactory.java:108)
at org.apache.commons.configuration.beanutils.DefaultBeanFactory.createBean(DefaultBeanFactory.java:64)
at org.apache.commons.configuration.beanutils.BeanHelper.createBean(BeanHelper.java:390)
... 2 more
The first thing I had to change was the layout of the XML file to match the documentations of the XMLBeanDeclaration and the XMLConfiguration:
<configuration>
<phonebook name="John,Mary" />
</configuration>
If I run the program then, it terminates normally and my output is:
phonebook[@name] : [John, Mary]
name : Mary
Mary
That means that the XMLConfiguration
correctly identifies the configuration file having an array in the attribute name, but somehow the XMLBeanDeclaration does not. It always just takes the last value.
I came up with a solution which works, more or less, by abusing the fact that it is possible to read in arrays as single String
s. I added the line config.setAttributeSplittingDisabled(true);
. The result is the following:
phonebook[@name] : John,Mary
name : John,Mary
John; Mary
In this case it seems that on creation of the bean the BeanHelper
realizes the array and is able to choose the correct method.
Since this makes my configuration a little bit unflexible, I wonder if it is possible to get the XMLBeanDeclaration
to handle arrays at an earlier stage to avoid the call to config.setAttributeSplittingDisabled(true);
?
I am using Java 8, Configuration 1.10 and BeanUtils 1.9.2.