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

JAXB实现同一标签下不同类型内容(字符串/SubContainer)映射至Java对象的疑问

XML转Java对象:处理ContainerValue的多类型存储问题

嘿,我看你在把带多命名空间的XML映射到Java对象时,卡在了让ContainerValue同时处理字符串值和SubContainer对象的问题上,咱们一步步来搞定这个事儿~

首先先回顾下你的XML示例:

<?xml version="1.0" encoding="utf-8"?>
<test:wallet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:test="http://www.w3.org/1999/xlink" xmlns:testTwo="http://google.com">
  <xsi:container name="container1">
    <test:val>hello-world</test:val>
  </xsi:container>
  <xsi:container name="container2">
    <test:val>
      <testTwo:subContainer name="subContainer1" value="hello-world-sub-container">
      </testTwo:subContainer>
    </test:val>
  </xsi:container>
</test:wallet>

还有你已经写好的三个Java类,咱们先补全并调整它们:

1. 先创建SubContainer类

这个类对应XML里的testTwo:subContainer元素,必须加上命名空间注解才能被JAXB识别:

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "subContainer", namespace = "http://google.com")
public class SubContainer {
    @XmlAttribute
    private String name;
    @XmlAttribute
    private String value;

    // JAXB需要getter和setter来访问属性,别忘了加上
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }
}

2. 调整ContainerValue类

要同时支持字符串和子对象,咱们用@XmlMixed结合@XmlAnyElement(lax=true),这样可以捕获元素内的混合内容(纯文本或子元素):

import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlMixed;
import java.util.List;

public class ContainerValue {
    // 用List<Object>存储混合内容:要么是字符串,要么是SubContainer对象
    @XmlMixed
    @XmlAnyElement(lax = true)
    private List<Object> content;

    // 方便获取实际内容的工具方法,过滤掉空的文本节点
    public Object getActualValue() {
        if (content == null || content.isEmpty()) {
            return null;
        }
        for (Object item : content) {
            // 跳过空的/纯空白的字符串节点
            if (!(item instanceof String) || !((String) item).trim().isEmpty()) {
                return item;
            }
        }
        return null;
    }

    // 标准的getter和setter
    public List<Object> getContent() {
        return content;
    }

    public void setContent(List<Object> content) {
        this.content = content;
    }
}

3. 补全Wallet类的命名空间注解

XML里的元素都带命名空间,JAXB必须明确知道每个前缀对应的命名空间才能正确映射:

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlNamespace;
import java.util.ArrayList;

@XmlRootElement(name = "wallet", namespace = "http://www.w3.org/1999/xlink")
// 声明XML中用到的所有命名空间
@XmlNamespace(prefix = "test", namespaceURI = "http://www.w3.org/1999/xlink")
@XmlNamespace(prefix = "xsi", namespaceURI = "http://www.w3.org/2001/XMLSchema-instance")
@XmlNamespace(prefix = "testTwo", namespaceURI = "http://google.com")
public class Wallet {
    @XmlElement(name = "container", namespace = "http://www.w3.org/2001/XMLSchema-instance")
    private ArrayList<Container> containers;

    public ArrayList<Container> getContainers() {
        return containers;
    }

    public void setContainers(ArrayList<Container> containers) {
        this.containers = containers;
    }
}

4. 调整Container类的命名空间

同样要给val元素加上对应的命名空间:

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;

public class Container {
    @XmlElement(name = "val", namespace = "http://www.w3.org/1999/xlink")
    private ContainerValue val;
    @XmlAttribute
    private String name;

    public ContainerValue getVal() {
        return val;
    }

    public void setVal(ContainerValue val) {
        this.val = val;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

测试解析效果

当你用JAXB解析XML后,可以这样判断内容类型:

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import java.io.File;

public class XmlParser {
    public static void main(String[] args) throws Exception {
        JAXBContext jaxbContext = JAXBContext.newInstance(Wallet.class, SubContainer.class);
        Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        Wallet wallet = (Wallet) unmarshaller.unmarshal(new File("your-xml-file.xml"));

        for (Container container : wallet.getContainers()) {
            Object value = container.getVal().getActualValue();
            if (value instanceof String) {
                System.out.println("容器 " + container.getName() + " 的值:" + value);
            } else if (value instanceof SubContainer) {
                SubContainer sub = (SubContainer) value;
                System.out.println("容器 " + container.getName() + " 的子容器:名称=" + sub.getName() + ",值=" + sub.getValue());
            }
        }
    }
}

这样运行后,就能正确识别第一个容器的字符串值,以及第二个容器的SubContainer对象了~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 16:02:37