如何转义JAXB生成的XML字符串双引号并解决RestTemplate提交报错
问题:RestTemplate POST XML时触发JAXB解析错误
问题场景
我需要构建XML并通过RestTemplate的POST请求提交,实现代码如下:
final RestTemplate restTemplate1 = new RestTemplate(); List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_XML)); messageConverters.add(converter); restTemplate1.setMessageConverters(messageConverters); HttpHeaders headers = new HttpHeaders(); headers.setBasicAuth("okmAdmin", "admin"); headers.setContentType(MediaType.APPLICATION_XML); JAXBContext context = null; context = JAXBContext.newInstance(Folder.class); Marshaller mar = null; mar = context.createMarshaller(); mar.setProperty("jaxb.encoding", "Unicode"); mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE); //Print XML String to Console StringWriter sw = new StringWriter(); Folder folder = new Folder(); folder.setPath("/okm:root/test4"); //Write XML to StringWriter mar.marshal(folder, sw); String xmlContent = sw.toString(); System.out.println(xmlContent); HttpEntity<String> request = new HttpEntity<String>(xmlContent, headers); ResponseEntity<Folder> response = restTemplate1.postForEntity("http://localhost:8080/OpenKM/services/rest/folder/create", request, Folder.class);
生成的XML内容:
<?xml version="1.0" encoding="Unicode" standalone="yes"?><folder><path>/okm:root/test4</path><permissions>0</permissions><subscribed>false</subscribed><hasChildren>false</hasChildren><style>0</style></folder>
提交时出现错误:
2022-12-05 16:50:21.121 ERROR 15764 --- [nio-8082-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException$BadRequest: 400 : "JAXBException occurred : Unexpected character '"' (code 34) in prolog; expected '<'<EOL> at [row,col {unknown-source}]: [1,1]. "] with root cause
错误原因
不是XML本身的双引号需要转义,而是实现逻辑冲突:
手动用JAXB把对象转成XML字符串后,又配置了MappingJackson2HttpMessageConverter处理XML。Jackson会把传入的XML字符串当成普通字符串处理,自动给整个XML内容包裹双引号,导致服务器收到的请求体开头是"<?xml...,解析时触发"prolog里意外的双引号"错误。
解决方案
方案1:使用JAXB消息转换器,自动处理XML序列化(推荐)
直接用Spring提供的Jaxb2RootElementHttpMessageConverter,无需手动转XML,让框架自动完成对象到XML的转换:
final RestTemplate restTemplate1 = new RestTemplate(); List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); // 替换为JAXB专用的XML消息转换器 Jaxb2RootElementHttpMessageConverter jaxbConverter = new Jaxb2RootElementHttpMessageConverter(); jaxbConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_XML)); messageConverters.add(jaxbConverter); restTemplate1.setMessageConverters(messageConverters); HttpHeaders headers = new HttpHeaders(); headers.setBasicAuth("okmAdmin", "admin"); headers.setContentType(MediaType.APPLICATION_XML); Folder folder = new Folder(); folder.setPath("/okm:root/test4"); // 直接传入Folder对象,框架自动序列化XML HttpEntity<Folder> request = new HttpEntity<>(folder, headers); ResponseEntity<Folder> response = restTemplate1.postForEntity( "http://localhost:8080/OpenKM/services/rest/folder/create", request, Folder.class );
方案2:保留手动XML转换,使用字符串消息转换器
如果必须手动生成XML字符串,就改用StringHttpMessageConverter,让框架直接发送原始字符串,不做额外处理:
final RestTemplate restTemplate1 = new RestTemplate(); List<HttpMessageConverter<?>> messageConverters = new ArrayList<>(); // 使用字符串转换器,直接发送XML原始内容 StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(Charset.forName("Unicode")); stringConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.APPLICATION_XML)); messageConverters.add(stringConverter); restTemplate1.setMessageConverters(messageConverters); HttpHeaders headers = new HttpHeaders(); headers.setBasicAuth("okmAdmin", "admin"); headers.setContentType(MediaType.APPLICATION_XML); JAXBContext context = JAXBContext.newInstance(Folder.class); Marshaller mar = context.createMarshaller(); mar.setProperty("jaxb.encoding", "Unicode"); mar.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.FALSE); StringWriter sw = new StringWriter(); Folder folder = new Folder(); folder.setPath("/okm:root/test4"); mar.marshal(folder, sw); String xmlContent = sw.toString(); HttpEntity<String> request = new HttpEntity<>(xmlContent, headers); ResponseEntity<Folder> response = restTemplate1.postForEntity( "http://localhost:8080/OpenKM/services/rest/folder/create", request, Folder.class );
内容的提问来源于stack exchange,提问作者user1999453
相关产品推荐
相关产品推荐

