如何在xjc/JAXB生成的XML中为多份XSD配置对应schemaLocation属性
解决方案
方法1:运行时动态注入schemaLocation属性(推荐,适合多场景复用)
无需修改生成的Java类逻辑,适配20份schema的批量配置需求。
- 新增XJC全局绑定文件,让所有生成的元素类自动添加任意属性存储字段。创建
global-binding.xjb文件,内容如下:
<jxb:bindings version="2.1" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" xmlns:annox="http://annox.dev.java.net"> <jxb:globalBindings> <!-- 给所有生成类添加@XmlAnyAttribute注解的Map字段,用于存储额外属性 --> <xjc:extraAnnotation> <annox:annotate> @javax.xml.bind.annotation.XmlAnyAttribute private java.util.Map<QName, Object> extraAttributes = new java.util.HashMap<>(); </annox:annotate> </xjc:extraAnnotation> </jxb:globalBindings> </jxb:bindings>
执行xjc命令时引入该绑定文件,生成的所有类都会自带可存储任意属性的Map字段。如果不想使用annox插件,也可以手动给生成类添加对应字段。
2. 实现Marshaller监听器,序列化前自动给对应元素注入schemaLocation属性:
import javax.xml.namespace.QName; import javax.xml.bind.Marshaller; import java.lang.reflect.Field; import java.util.Map; // 提前维护所有类和对应schemaLocation的映射 private static final Map<Class<?>, String> CLASS_SCHEMA_MAPPING = Map.of( GovTalkMessage.class, "http://xmlgw.companieshouse.gov.uk/v1-0/schema/Egov_ch.xsd", FormSubmission.class, "http://xmlgw.companieshouse.gov.uk/v1-0/schema/forms/FormSubmission-v2-11.xsd", ReturnofAllotmentShares.class, "http://xmlgw.companieshouse.gov.uk/v1-0/schema/forms/ReturnofAllotmentShares-v3-0.xsd" // 剩余17个schema对应的类按规则补充即可 ); private static final QName XSI_SCHEMA_LOCATION = new QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi"); Marshaller.Listener schemaInjectListener = new Marshaller.Listener() { @Override public void beforeMarshal(Object source) { if (CLASS_SCHEMA_MAPPING.containsKey(source.getClass())) { try { Field extraField = source.getClass().getDeclaredField("extraAttributes"); extraField.setAccessible(true); Map<QName, Object> attrMap = (Map<QName, Object>) extraField.get(source); attrMap.put(XSI_SCHEMA_LOCATION, CLASS_SCHEMA_MAPPING.get(source.getClass())); } catch (Exception e) { // 自行补充异常处理逻辑 e.printStackTrace(); } } } };
- 配置Marshaller启用监听器,完成序列化:
JAXBContext context = JAXBContext.newInstance(GovTalkMessage.class); Marshaller marshaller = context.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 触发xsi命名空间自动生成 marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, ""); marshaller.setListener(schemaInjectListener); // 执行序列化 marshaller.marshal(yourGovTalkMessageObj, outputStream);
方法2:生成类时固定注入schemaLocation属性(适合schema固定不常变更的场景)
直接通过绑定文件给每个元素类添加固定的schemaLocation属性,无需运行时处理。绑定文件示例如下:
<jxb:bindings version="2.1" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" xmlns:annox="http://annox.dev.java.net"> <!-- 给ReturnofAllotmentShares类添加固定schemaLocation属性 --> <jxb:bindings schemaLocation="http://xmlgw.companieshouse.gov.uk/v1-0/schema/forms/ReturnofAllotmentShares-v3-0.xsd" node="//xs:element[@name='ReturnofAllotmentShares']"> <xjc:extraAnnotation> <annox:annotate> @javax.xml.bind.annotation.XmlAttribute(name = "schemaLocation", namespace = "http://www.w3.org/2001/XMLSchema-instance") private final String schemaLocation = "http://xmlgw.companieshouse.gov.uk/v1-0/schema/forms/ReturnofAllotmentShares-v3-0.xsd"; </annox:annotate> </xjc:extraAnnotation> </jxb:bindings> <!-- 其他元素类按相同规则配置即可,20份schema可以写简单脚本批量生成绑定规则 --> </jxb:bindings>
如果使用Jakarta JAXB高版本,只需要把代码中javax.xml.bind包名替换为jakarta.xml.bind即可。
内容的提问来源于stack exchange,提问作者Rowan Marshall
相关产品推荐
相关产品推荐

