You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JAXB键值对编组:值支持嵌套的XMLAdapter实现方案咨询

解决Parameter类的XML多类型Value适配问题

先明确场景:我定义了一个Parameter类,用来映射XML中的键值节点,代码如下:

public class Parameter implements Serializable
{
    @XmlElement(name = "Key")
    protected String key;
    @XmlElement(name = "Value")
    protected Object value;
}

其中value字段支持两种类型:

  • 字符串类型,此时Parameter等价于Map<String, String>的键值对
  • 另一个Parameter对象(甚至是嵌套多层的Parameter列表)

对应的合法XML示例有两种:

单层级键值对

<Parameter>
<Key>MyKey</Key>
<Value>MyValue</Value>
</Parameter>

嵌套多层的结构

<Parameter>
<Key>Key1</Key>
<Value>
    <Key>Key2</Key>
    <Value>
        <Key>Key3</Key>
        <Value>ValueInString1</Value>
    </Value>
    <Value>
        <Key>Key4</Key>
        <Value>ValueInString2</Value>
    </Value>
</Value>

现在要实现XMLAdapter来处理这种多类型场景,之前的两种思路卡点可以这样解决:

思路2:仅为Value属性使用适配器(更推荐)

卡点是当value为Parameter对象时,无法调用通用编组器。解决方法是在适配器中获取JAXB上下文,直接复用Parameter的默认编组逻辑:

  1. 先给value字段加上@XmlJavaTypeAdapter注解,指定适配器:
public class Parameter implements Serializable
{
    @XmlElement(name = "Key")
    protected String key;
    @XmlElement(name = "Value")
    @XmlJavaTypeAdapter(ValueAdapter.class)
    protected Object value;
}
  1. 实现ValueAdapter,在适配逻辑中区分类型,遇到Parameter时用JAXB上下文来编组/解组:
public class ValueAdapter extends XmlAdapter<Object, Object> {
    private transient JAXBContext jaxbContext;

    private JAXBContext getJaxbContext() throws JAXBException {
        if (jaxbContext == null) {
            jaxbContext = JAXBContext.newInstance(Parameter.class);
        }
        return jaxbContext;
    }

    @Override
    public Object unmarshal(Object v) throws Exception {
        if (v instanceof Element) {
            Element element = (Element) v;
            if ("Parameter".equals(element.getNodeName())) {
                return getJaxbContext().createUnmarshaller().unmarshal(element);
            }
        }
        return v;
    }

    @Override
    public Object marshal(Object v) throws Exception {
        if (v instanceof Parameter) {
            Marshaller marshaller = getJaxbContext().createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
            StringWriter writer = new StringWriter();
            marshaller.marshal(v, writer);
            
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            return db.parse(new InputSource(new StringReader(writer.toString()))).getDocumentElement();
        }
        return v;
    }
}

这样就能自动区分value的类型,复用JAXB对Parameter的默认处理逻辑,不用手动写嵌套结构的编组代码。

思路1:为整个Parameter类使用适配器

卡点是编组键值对列表,解决方法是定义一个中间适配类,用来映射XML的结构,再在适配器中做Parameter和中间类的转换:

  1. 定义中间类ParameterXml,用来对应XML中的结构:
public class ParameterXml implements Serializable {
    @XmlElement(name = "Key")
    private String key;
    @XmlElement(name = "Value")
    private List<Object> values;

    // 构造器、getter、setter省略
}
  1. 实现ParameterAdapter,处理Parameter和ParameterXml的互转:
public class ParameterAdapter extends XmlAdapter<ParameterXml, Parameter> {
    @Override
    public Parameter unmarshal(ParameterXml xml) throws Exception {
        Parameter param = new Parameter();
        param.setKey(xml.getKey());
        if (xml.getValues() != null && !xml.getValues().isEmpty()) {
            if (xml.getValues().size() == 1) {
                Object value = xml.getValues().get(0);
                if (value instanceof ParameterXml) {
                    param.setValue(unmarshal((ParameterXml) value));
                } else {
                    param.setValue(value);
                }
            } else {
                List<Parameter> subParams = new ArrayList<>();
                for (Object val : xml.getValues()) {
                    if (val instanceof ParameterXml) {
                        subParams.add(unmarshal((ParameterXml) val));
                    }
                }
                param.setValue(subParams);
            }
        }
        return param;
    }

    @Override
    public ParameterXml marshal(Parameter param) throws Exception {
        ParameterXml xml = new ParameterXml();
        xml.setKey(param.getKey());
        List<Object> values = new ArrayList<>();
        Object value = param.getValue();
        if (value instanceof Parameter) {
            values.add(marshal((Parameter) value));
        } else if (value instanceof List) {
            for (Object item : (List<?>) value) {
                if (item instanceof Parameter) {
                    values.add(marshal((Parameter) item));
                }
            }
        } else {
            values.add(value);
        }
        xml.setValues(values);
        return xml;
    }
}

然后给Parameter类加上@XmlJavaTypeAdapter(ParameterAdapter.class)注解,就能完成整个类的适配。

两种思路对比:思路2更聚焦于问题核心(Value的多类型),代码更简洁,复用JAXB原生逻辑;思路1需要手动处理嵌套和列表结构,适合需要完全自定义XML结构的场景。

内容的提问来源于stack exchange,提问作者dtchmnt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 17:15:29