Java / JAXB:将XML属性解组为特定的Java对象属性
有一个丑陋的XML文件必须是unmarshalled:
8081 WARNING 64M yes
生成的Java对象应该是:
public class DefaultOptions { private int defaultPort; private String logLevel; // etc... } public class CustomOptions { private String memory; private String compatibility; // etc... }
这个问题的答案非常接近,但我无法弄清楚最终的解决方案。
怎么样?
介绍一个名为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; } }
然后在包含选项列表的类(本示例中为Configuration)中,在该属性上指定@XmlJavaTypeAdapter:
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 section = new ArrayList (); @XmlJavaTypeAdapter(OptionsAdapter.class) public List getSection() { return section; } public void setSection(List section) { this.section = section; } }
XmlAdapter看起来像这样:
import javax.xml.bind.annotation.adapters.XmlAdapter; public class OptionsAdapter extends XmlAdapter { @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看起来像:
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 = new ArrayList (); Map map = new HashMap(); public void beforeMarshal(Marshaller marshaller) { for(Entry 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; } }
您可以创建一个单独的类来表示XML的结构:
public class Section { @XmlAttribute public String name; @XmlElement(name = "value") public List values; } public class Value { @XmlAttribute public String name; @XmlValue public String value; }
然后使用XmlAdapter
执行转换:
public class OptionsAdapter extends XmlAdapter { 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; } public class DefaultOptions extends Options { ... } public class CustomOptions extends Options { ... }