如何基于method的name属性解组相似包装的XML到POJO?
可行实现方案
方案一:使用XmlAdapter自定义转换逻辑
XmlAdapter可以让你手动控制XML节点到POJO的转换流程,刚好适配根据method的name属性选择对应子类的场景。
1. 编写适配器类
创建继承自XmlAdapter<MethodAdapter.AdaptedMethod, VeraWSMethod>的适配器,在unmarshal方法里根据name属性判断实例化的子类:
public class MethodAdapter extends XmlAdapter<MethodAdapter.AdaptedMethod, VeraWSMethod> { // 适配类,映射XML中的method节点 @XmlRootElement(name = "method") public static class AdaptedMethod { @XmlAttribute public String name; @XmlAnyElement(lax = true) public List<Object> content; } @Override public VeraWSMethod unmarshal(AdaptedMethod adaptedMethod) throws Exception { VeraWSMethod method; // 根据method的name属性选择对应子类 switch (adaptedMethod.name) { case "place-info": method = new MethodXX1(); break; case "another-method": method = new MethodXX2(); break; default: throw new IllegalArgumentException("未知method名称: " + adaptedMethod.name); } // 将适配类的内容解组到目标对象 JAXBContext ctx = JAXBContext.newInstance(method.getClass()); Unmarshaller unmarshaller = ctx.createUnmarshaller(); for (Object node : adaptedMethod.content) { if (node instanceof Element) { unmarshaller.unmarshal((Element) node, method.getClass()); } } method.setName(adaptedMethod.name); return method; } @Override public AdaptedMethod marshal(VeraWSMethod method) throws Exception { // 如需编组XML,实现反向转换逻辑 AdaptedMethod adapted = new AdaptedMethod(); adapted.name = method.getName(); JAXBContext ctx = JAXBContext.newInstance(method.getClass()); Marshaller marshaller = ctx.createMarshaller(); DOMResult result = new DOMResult(); marshaller.marshal(method, result); Node root = result.getNode(); if (root instanceof Document) { adapted.content = Collections.singletonList(((Document) root).getDocumentElement()); } return adapted; } }
2. 在VeraWSClass中引用适配器
给method字段的getter添加@XmlJavaTypeAdapter注解:
@XmlRootElement(name = "class") public class VeraWSClass { // 原有属性的getter/setter保持不变 @XmlJavaTypeAdapter(MethodAdapter.class) public VeraWSMethod getMethod() { return method; } public void setMethod(VeraWSMethod method) { this.method = method; } }
方案二:自定义Unmarshaller.Listener监听解组过程
通过监听解组完成事件,在afterUnmarshal方法里根据method的属性替换实例:
@XmlRootElement(name = "class") public class VeraWSClass { private String name; private String version; private String revision; // 临时接收method节点的适配类 private TempMethod tempMethod; // 最终的method对象 private VeraWSMethod method; // 原有属性的getter/setter不变 @XmlElement(name = "method") public TempMethod getTempMethod() { return tempMethod; } public void setTempMethod(TempMethod tempMethod) { this.tempMethod = tempMethod; } // 解组完成后自动触发的方法 private void afterUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException { if (tempMethod == null) return; JAXBContext ctx = JAXBContext.newInstance(MethodXX1.class, MethodXX2.class); Unmarshaller methodUnmarshaller = ctx.createUnmarshaller(); // 根据name属性转换为对应子类 switch (tempMethod.name) { case "place-info": method = methodUnmarshaller.unmarshal(tempMethod.getSource(), MethodXX1.class).getValue(); break; case "another-method": method = methodUnmarshaller.unmarshal(tempMethod.getSource(), MethodXX2.class).getValue(); break; default: throw new IllegalArgumentException("未知method: " + tempMethod.name); } tempMethod = null; } // 临时类,捕获method的原始XML结构 @XmlRootElement(name = "method") public static class TempMethod { @XmlAttribute public String name; @XmlAnyElement private List<Element> elements; public Source getSource() { Document doc = elements.get(0).getOwnerDocument(); Element methodElement = doc.createElement("method"); methodElement.setAttribute("name", name); for (Element elem : elements) { methodElement.appendChild(doc.importNode(elem, true)); } return new DOMSource(methodElement); } } // 对外暴露实际的method对象(无需XML注解) public VeraWSMethod getMethod() { return method; } public void setMethod(VeraWSMethod method) { this.method = method; } }
方案三:结合@XmlSeeAlso与动态类型判断
如果子类都已配置JAXB注解,可以用@XmlSeeAlso声明所有子类,解组后再根据name属性强转类型:
1. 在父类添加@XmlSeeAlso
@XmlSeeAlso({MethodXX1.class, MethodXX2.class}) public abstract class VeraWSMethod { @XmlAttribute protected String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
2. 修改VeraWSClass的method字段
直接用@XmlElement映射,解组后手动判断类型:
@XmlRootElement(name = "class") public class VeraWSClass { // 原有属性不变 @XmlElement(name = "method") public VeraWSMethod getMethod() { return method; } public void setMethod(VeraWSMethod method) { this.method = method; } // 新增获取具体类型的方法 public <T extends VeraWSMethod> T getConcreteMethod(Class<T> clazz) { if (clazz.isInstance(method)) { return clazz.cast(method); } throw new ClassCastException("method类型不匹配: " + clazz.getName()); } }
使用示例
解组完成后根据name属性处理对应子类:
VeraWSClass wsClass = unmarshaller.unmarshal(xmlSource, VeraWSClass.class).getValue(); VeraWSMethod method = wsClass.getMethod(); if ("place-info".equals(method.getName())) { MethodXX1 placeInfo = (MethodXX1) method; // 处理place-info的业务逻辑 } else if ("another-method".equals(method.getName())) { MethodXX2 anotherMethod = (MethodXX2) method; // 处理another-method的业务逻辑 }
内容的提问来源于stack exchange,提问作者František Slabý
相关产品推荐
相关产品推荐

