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
相关产品推荐
相关产品推荐

