Java/JAXB: Unmarshall XML attributes to specific Java object attributes
Solution 1:
How about?
Introduce a common super class called Options:
import javax.xml.bind.annotation.XmlAttribute;
public abstract class Options {
private String name;
@XmlAttribute
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Then on your class with the list of options (Configuration in this example), specify an @XmlJavaTypeAdapter on that property:
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Configuration {
private List<Options> section = new ArrayList<Options>();
@XmlJavaTypeAdapter(OptionsAdapter.class)
public List<Options> getSection() {
return section;
}
public void setSection(List<Options> section) {
this.section = section;
}
}
The XmlAdapter will look something like this:
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class OptionsAdapter extends XmlAdapter<AdaptedOptions, Options> {
@Override
public Options unmarshal(AdaptedOptions v) throws Exception {
if("default_options".equals(v.name)) {
DefaultOptions options = new DefaultOptions();
options.setName(v.getName());
options.setDefaultPort(Integer.valueOf(v.map.get("default_port")));
options.setLogLevel(v.map.get("log_level"));
return options;
} else {
CustomOptions options = new CustomOptions();
options.setName(v.getName());
options.setCompatibility(v.map.get("compatibility"));
options.setMemory(v.map.get("memory"));
return options;
}
}
@Override
public AdaptedOptions marshal(Options v) throws Exception {
AdaptedOptions adaptedOptions = new AdaptedOptions();
adaptedOptions.setName(v.getName());
if(DefaultOptions.class == v.getClass()) {
DefaultOptions options = (DefaultOptions) v;
adaptedOptions.map.put("default_port", String.valueOf(options.getDefaultPort()));
adaptedOptions.map.put("log_level", options.getLogLevel());
} else {
CustomOptions options = (CustomOptions) v;
adaptedOptions.map.put("compatibility", options.getCompatibility());
adaptedOptions.map.put("memory", options.getMemory());
}
return adaptedOptions;
}
}
AdaptedOptions looks like:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlValue;
public class AdaptedOptions extends Options {
@XmlAttribute String name;
@XmlElement List<Value> value = new ArrayList<Value>();
Map<String, String> map = new HashMap<String, String>();
public void beforeMarshal(Marshaller marshaller) {
for(Entry<String, String> entry : map.entrySet()) {
Value aValue = new Value();
aValue.name = entry.getKey();
aValue.value = entry.getValue();
value.add(aValue);
}
}
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
for(Value aValue : value) {
map.put(aValue.name, aValue.value);
}
}
private static class Value {
@XmlAttribute String name;
@XmlValue String value;
}
}
Solution 2:
You may create a separate classes to represent structure of your XML:
public class Section {
@XmlAttribute
public String name;
@XmlElement(name = "value")
public List<Value> values;
}
public class Value {
@XmlAttribute
public String name;
@XmlValue
public String value;
}
and then use an XmlAdapter
to perform conversion:
public class OptionsAdapter extends XmlAdapter<Section, Options> {
public Options unmarshal(Section s) {
if ("default_options".equals(s.name)) {
...
} else if (...) {
...
}
...
}
...
}
@XmlElement
public class Configuration {
@XmlElement(name = "section")
@XmlJavaTypeAdapter(OptionsAdapter.class)
public List<Options> options;
}
public class DefaultOptions extends Options { ... }
public class CustomOptions extends Options { ... }