XML上传接口必须用@RequestBody。因JAXB只能绑定整个请求体,@RequestParam会错误解析XML导致截断、乱码或JAXBException;需配Content-Type: application/xml,Spring Boot 2.3+需手动引入jaxb-api和jaxb-runtime依赖。

XML上传接口必须用 @RequestBody 还是 @RequestParam?
必须用 @RequestBody。JAXB 只能绑定整个请求体,而 @RequestParam 会把 XML 当作普通表单参数解析(触发 URL 解码、截断、乱码),导致 JAXBException: unexpected XML tag 或空对象。
-
前端需设置
Content-Type: application/xml - Controller 方法参数加
@RequestBody,不要加@XmlRootElement注解在参数类型上——那是给 JAXB 类自己用的,不是给 Spring 绑定用的 - Spring Boot 2.3+ 默认移除了 JAXB 依赖,需手动添加:
javax.xml.bind jaxb-api org.glassfish.jaxb jaxb-runtime
如何让 JAXB 自动校验 XML 是否符合 XSD?
不能只靠注解(如 @XmlElement(required = true))做运行时校验——它只校验 Java 对象构造,不校验原始 XML 结构。真正生效的是 SchemaFactory + Unmarshaller.setSchema()。
- 把 XSD 文件放在
src/main/resources/xsd/order.xsd - 在
@Configuration类中定义Marshaller和UnmarshallerBean,并注入 Schema:
@Bean
public Unmarshaller unmarshaller() throws Exception {
JAXBContext context = JAXBContext.newInstance(Order.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(new ClassPathResource("xsd/order.xsd").getURL());
unmarshaller.setSchema(schema); // 关键:开启校验
return unmarshaller;
}
- Controller 中使用该
Unmarshaller手动解析(不走默认 Spring 的自动绑定):
@PostMapping("/upload")
public ResponseEntity upload(@RequestBody String xml) {
try {
StringReader reader = new StringReader(xml);
Order order = (Order) unmarshaller.unmarshal(reader); // 校验在此触发
return ResponseEntity.ok("OK");
} catch (JAXBException e) {
return ResponseEntity.badRequest().body("XML 校验失败:" + e.getMessage());
}
}
ValidationEventHandler 怎么捕获具体哪一行出错?
默认异常只报 “invalid content”,没行号、没元素名。必须自定义 ValidationEventHandler 并传给 Unmarshaller。
- 实现事件处理器,记录
SAXParseException的详细信息:
public class XmlValidationErrorHandler implements ValidationEventHandler {
private final List errors = new ArrayList<>();
@Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == ValidationEvent.ERROR ||
event.getSeverity() == ValidationEvent.FATAL_ERROR) {
Throwable cause = event.getLinkedException();
if (cause instanceof SAXParseException) {
SAXParseException spe = (SAXParseException) cause;
errors.add(String.format("第%d行第%d列:%s",
spe.getLineNumber(), spe.getColumnNumber(), event.getMessage()));
}
}
return false; // 返回 false 表示中断解析
}
public List getErrors() { return errors; }
}
- 在
unmarshaller()Bean 中设置它:
unmarshaller.setEventHandler(new XmlValidationErrorHandler());
- Controller 中捕获并返回错误列表,而不是只抛异常
为什么用了 @XmlSchema 和 @XmlAccessorType 还是反序列化失败?
常见原因是命名空间(namespace)不匹配。JAXB 默认严格校验 namespace,但很多上传的 XML 没声明 xmlns,或用了别名(如 xmlns:x="http://example.com")。
- 如果 XSD 定义了
targetNamespace,Java 类必须用@XmlSchema(namespace = "...")声明,且@XmlRootElement要加namespace属性 - 更稳妥的做法:在
Unmarshaller上关闭 namespace 校验(仅测试/内部系统适用):
unmarshaller.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() {
@Override
public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
return "";
}
});
// 同时设置:
unmarshaller.setEventHandler(...);
unmarshaller.setSchema(schema); // 仍保留结构校验,只绕过 namespace
实际生产环境建议统一 XML 命名空间,而不是关校验——否则 XSD 就失去约束意义了。










