【问题标题】:Java/JAXB: Unmarshall XML attributes to specific Java object attributesJava/JAXB:将 XML 属性解组为特定的 Java 对象属性
【发布时间】:2011-03-18 02:53:36
【问题描述】:

有一个丑陋的 XML 文件需要解组:

<?xml version="1.0" ?>
<configuration>
    <section name="default_options">
        <value name="default_port">8081</value>
        <value name="log_level">WARNING</value>
    </section>
    <section name="custom_options">
        <value name="memory">64M</value>
        <value name="compatibility">yes</value>
    </section>
</configuration>

生成的 Java 对象应该是:

public class DefaultOptions {
    private int defaultPort;
    private String logLevel;
    // etc...
}

public class CustomOptions {
    private String memory;
    private String compatibility;
    // etc...
}

This问题的答案非常接近,但我无法弄清楚最终的解决方案。

【问题讨论】:

  • 您不能更改生成的 java 对象吗?恕我直言,如果您只是遵循 xml 结构(或将其更改为遵循您想要的结构)会更容易和更清洁
  • @Diego Dias,实际上没有。必须有这样的 POJO。
  • 可以在 DefaultOptions & CustomOptions 中添加通用超类吗?

标签: java xml jaxb


【解决方案1】:

您可以创建一个单独的类来表示您的 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;
}

然后使用XmlAdapter进行转换:

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 { ... }

【讨论】:

  • 有趣的解决方案。首先,谢谢。我会试试看。这是解决问题的唯一方法吗?或者也许有一些更优雅的解决方案没有“if ("default_options".equals(s.name))”?
  • @Artyom:由于 JAXB 无法从属性值中推断出目标类,因此无论如何它都需要某种手动调配。当然,你可以用更抽象的方式来做,例如使用从属性值到选项工厂的映射。
【解决方案2】:

怎么样?

引入一个通用的超类,叫做 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;
    }

}

然后在带有选项列表的类(本例中的配置)上,在该属性上指定一个 @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<Options> section = new ArrayList<Options>();

    @XmlJavaTypeAdapter(OptionsAdapter.class)
    public List<Options> getSection() {
        return section;
    }

    public void setSection(List<Options> section) {
        this.section = section;
    }

}

XmlAdapter 看起来像这样:

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 看起来像:

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;
    }

}

【讨论】:

    猜你喜欢
    • 2011-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 2014-09-18
    • 1970-01-01
    相关资源
    最近更新 更多