使用Jaxb2Marshaller编组时如何为非根XML元素添加schemaLocation
<oai:oai-identifier>元素自动添加schemaLocation的问题 问题背景
使用Spring Boot 3/Java 17开发符合Open Archives Harvesting规范的OAI-PMH服务,通过OAI-PMH.xsd等Schema文件生成Java类,借助Spring的MarshallingHttpMessageConverter结合Jaxb2Marshaller输出XML响应。但编组后的XML无法通过验证,报错信息如下:
cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'oai:oai-identifier'.xml(cvc-complex-type.2.4.c)
经排查,问题出在非根元素<oai:oai-identifier>缺少正确的xsi:schemaLocation声明,手动为该元素添加后验证通过,现需实现编组过程中自动为该元素注入schemaLocation。
当前配置代码:
@Configuration class WebConfig implements WebMvcConfigurer { @Override public void extendMessageConverters( @NonNull List<HttpMessageConverter<?>> converters) { var marshaller = new Jaxb2Marshaller(); marshaller.setContextPaths( "org.openarchives.oai", "org.openarchives.oai_dc", "org.openarchives.oai_identifier", "org.purl.dc.elements"); Map<String, ?> marshallerProperties = new HashMap<>() { { put(Marshaller.JAXB_FORMATTED_OUTPUT, true); put(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd"); } }; marshaller.setMarshallerProperties(marshallerProperties); var converter = new MarshallingHttpMessageConverter(marshaller); converter.setDefaultCharset(StandardCharsets.UTF_8); converters.add(0, converter); } }
解决方案
1. 补充完整的schemaLocation映射
当前仅配置了OAI-PMH主Schema的位置,需将oai-identifier对应的命名空间和Schema地址加入Marshaller.JAXB_SCHEMA_LOCATION属性,多组映射用空格分隔:
put(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd " + "http://www.openarchives.org/OAI/2.0/oai-identifier http://www.openarchives.org/OAI/2.0/oai-identifier.xsd");
2. 验证JAXB生成类的命名空间注解
检查org.openarchives.oai_identifier包下的生成类(如OaiIdentifier)是否带有正确的命名空间注解,示例如下:
@XmlRootElement(name = "oai-identifier", namespace = "http://www.openarchives.org/OAI/2.0/oai-identifier") public class OaiIdentifier { // 类内容 }
若生成类无正确注解,需重新通过XJC工具生成(指定命名空间参数)或手动补充注解。
3. 修改后的完整配置代码
@Configuration class WebConfig implements WebMvcConfigurer { @Override public void extendMessageConverters( @NonNull List<HttpMessageConverter<?>> converters) { var marshaller = new Jaxb2Marshaller(); marshaller.setContextPaths( "org.openarchives.oai", "org.openarchives.oai_dc", "org.openarchives.oai_identifier", "org.purl.dc.elements"); Map<String, ?> marshallerProperties = new HashMap<>() { { put(Marshaller.JAXB_FORMATTED_OUTPUT, true); put(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd " + "http://www.openarchives.org/OAI/2.0/oai-identifier http://www.openarchives.org/OAI/2.0/oai-identifier.xsd"); } }; marshaller.setMarshallerProperties(marshallerProperties); var converter = new MarshallingHttpMessageConverter(marshaller); converter.setDefaultCharset(StandardCharsets.UTF_8); converters.add(0, converter); } }
原理说明
JAXB的JAXB_SCHEMA_LOCATION属性支持同时配置多组命名空间与Schema文件的映射关系,当编组过程中遇到对应命名空间下的元素时,会自动在根元素(或对应元素的父节点)注入包含该映射的xsi:schemaLocation属性,确保XML验证时能找到<oai:oai-identifier>对应的Schema定义。
内容的提问来源于stack exchange,提问作者user25032322

