XMLAdapter技术问询:能否在适配器内部调用默认marshal方法?
两种XMLAdapter实现方案(避免手动逐个赋值)
针对你需要给Node2动态补充description属性的场景,以下是两种XMLAdapter的具体实现方式,都能复用JAXB默认的marshal/unmarshal逻辑,无需手动逐个赋值:
方案1:类型级别XMLAdapter
这种方式直接在Node2类上绑定适配器,序列化时统一补充description属性,逻辑更集中。
步骤1:定义Node2模型类
包含原XML的id属性,以及最终XML需要的description属性:
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlJavaTypeAdapter(Node2Adapter.class) public class Node2 { @XmlAttribute private String id; @XmlAttribute // 仅在最终XML输出 private String description; // Getter & Setter public String getId() { return id; } public void setId(String id) { this.id = id; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
步骤2:实现类型级适配器
适配器继承XmlAdapter<Node2, Node2>,复用默认的反序列化逻辑,仅在序列化时补充description:
import javax.xml.bind.annotation.adapters.XmlAdapter; public class Node2Adapter extends XmlAdapter<Node2, Node2> { // 反序列化:直接返回JAXB默认解析后的对象(原XML无description,属性为null) @Override public Node2 unmarshal(Node2 v) throws Exception { return v; } // 序列化:根据id生成description后返回对象 @Override public Node2 marshal(Node2 v) throws Exception { // 这里替换成你的实际逻辑(比如从数据库/配置获取对应id的描述) String generatedDesc = "Node2-" + v.getId() + "的描述内容"; v.setDescription(generatedDesc); return v; } }
使用方式
正常初始化JAXB的Marshaller/Unmarshaller即可,适配器会自动生效:
// 反序列化原XML Unmarshaller unmarshaller = JAXBContext.newInstance(Root.class).createUnmarshaller(); Root root = (Root) unmarshaller.unmarshal(new File("原XML路径")); // 序列化最终XML Marshaller marshaller = JAXBContext.newInstance(Root.class).createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(root, new File("最终XML路径"));
方案2:字段级别XMLAdapter
如果仅需要针对description属性单独处理,可采用字段级适配器,通过ThreadLocal传递当前Node2对象以获取id。
步骤1:定义Node2模型类
给description字段绑定适配器:
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; public class Node2 { @XmlAttribute private String id; @XmlAttribute @XmlJavaTypeAdapter(Node2DescriptionAdapter.class) private String description; // Getter & Setter public String getId() { return id; } public void setId(String id) { this.id = id; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
步骤2:实现字段级适配器
利用ThreadLocal存储当前序列化的Node2对象,生成对应的description:
import javax.xml.bind.annotation.adapters.XmlAdapter; public class Node2DescriptionAdapter extends XmlAdapter<String, String> { private static final ThreadLocal<Node2> CURRENT_NODE = new ThreadLocal<>(); // 反序列化:原XML无该属性,直接返回null @Override public String unmarshal(String v) throws Exception { return null; } // 序列化:从ThreadLocal获取当前Node2,根据id生成描述 @Override public String marshal(String v) throws Exception { Node2 currentNode = CURRENT_NODE.get(); if (currentNode != null) { return "Node2-" + currentNode.getId() + "的描述内容"; } return null; } // 供外部设置当前Node2的静态方法 public static void setCurrentNode(Node2 node) { CURRENT_NODE.set(node); } // 清理ThreadLocal,防止内存泄漏 public static void clearCurrentNode() { CURRENT_NODE.remove(); } }
步骤3:配置Marshaller监听器
在序列化时通过监听器设置/清理ThreadLocal中的Node2对象:
Marshaller marshaller = JAXBContext.newInstance(Root.class).createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 添加监听器,捕获Node2的序列化前后事件 marshaller.setListener(new Marshaller.Listener() { @Override public void beforeMarshal(Object source) { if (source instanceof Node2) { Node2DescriptionAdapter.setCurrentNode((Node2) source); } } @Override public void afterMarshal(Object source) { if (source instanceof Node2) { Node2DescriptionAdapter.clearCurrentNode(); } } }); // 执行序列化 marshaller.marshal(root, new File("最终XML路径"));
方案对比
- 方案1:代码更简洁,逻辑集中,适合对整个Node2类型做统一处理,无需额外监听器,推荐优先使用。
- 方案2:灵活性更高,仅针对单个属性生效,但需要配合ThreadLocal和监听器,适合局部属性的特殊处理场景。
内容的提问来源于stack exchange,提问作者Adriano
相关产品推荐
相关产品推荐

