如何避免Sales Cloud V2 Get操作中的反序列化错误
处理Sales Cloud V2非法URI格式的反序列化问题
我们通过Get操作将Sales Cloud V2系统的数据同步至Java微服务,使用SAP Cloud SDK OpenAPI生成器实现,整体运行正常。Sales Cloud V2中存在Account实体,已基于其OpenAPI定义在Java应用中创建了API客户端及模型,其中Account模型的web属性定义如下:
web string($uri) maxLength: 1280 minLength: 1 title: Website
但Sales Cloud V2系统中有一个Account的web属性值为带双引号的"www.123.info",属于系统维护错误。执行Get操作获取该Account数据时触发如下异常:
org.springframework.web.client.RestClientException: Error while extracting response for type [class com.sap.mkt.b2e.salescloudv2.model.Accountqueryresponse] and content type [application/json;charset=utf-8]; nested exception is org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `java.net.URI` from String ""www.123.info"": not a valid textual representation, problem: Illegal character in path at index 0: "www.123.info"; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.net.URI` from String ""www.123.info"": not a valid textual representation, problem: Illegal character in path at index 0: "www.123.info"
通过Postman获取数据时,JSON响应中该字段为:
... "correspondenceLanguage": "en", "eMail": "xy.z@sap.com", "web": "\"www.123.info\"", "id": "11ed932d-90e9-932e-afdb-81255ba8c000", "parentId": "11ed932d-90e9-1dfe-afdb-81255ba8c000", ...
该JSON无法正常解析引发异常,可通过以下几种方式添加校验或过滤逻辑,确保值能正常反序列化:
解决方案
方法1:自定义Jackson反序列化器处理脏数据
创建自定义URI反序列化器,在反序列化阶段自动清理字符串首尾的双引号,再尝试转换为URI:
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; public class CleanUriDeserializer extends StdDeserializer<URI> { public CleanUriDeserializer() { super(URI.class); } @Override public URI deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { String value = p.getText().trim(); // 去除首尾双引号 if (value.startsWith("\"") && value.endsWith("\"")) { value = value.substring(1, value.length() - 1); } try { return new URI(value); } catch (URISyntaxException e) { // 处理仍不合法的情况,可返回null或抛出自定义异常 return null; } } }
在Account模型的web字段上添加注解,指定使用该反序列化器:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; public class Accountqueryresponse { // 其他字段定义... @JsonDeserialize(using = CleanUriDeserializer.class) private URI web; // getter和setter方法... }
方法2:修改OpenAPI生成器配置,生成String类型字段后手动转换
若允许调整模型结构,可修改OpenAPI生成器配置,将web字段生成为String类型,再在业务代码中手动清理并转换为URI:
- 在OpenAPI生成器配置文件(如
openapi-generator.yml)中添加类型映射:
typeMappings: uri: String
- 重新生成API客户端及模型,此时
web字段为String类型 - 在业务代码中处理转换逻辑:
public URI cleanAndConvertToUri(String webStr) { if (webStr == null) return null; String cleaned = webStr.trim(); if (cleaned.startsWith("\"") && cleaned.endsWith("\"")) { cleaned = cleaned.substring(1, cleaned.length() - 1); } try { return new URI(cleaned); } catch (URISyntaxException e) { // 根据业务需求处理异常,比如返回null或记录日志 return null; } }
方法3:添加全局响应拦截器预处理JSON
在Spring的RestTemplate或WebClient中添加拦截器,在反序列化前修改响应体,清理web字段中的非法双引号:
以RestTemplate为例,实现自定义响应处理器:
import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.ResponseErrorHandler; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import org.springframework.http.HttpStatus; public class CleanWebUriResponseHandler implements ResponseErrorHandler { private final ResponseErrorHandler delegate; public CleanWebUriResponseHandler(ResponseErrorHandler delegate) { this.delegate = delegate; } @Override public boolean hasError(ClientHttpResponse response) throws IOException { return delegate.hasError(response); } @Override public void handleError(ClientHttpResponse response) throws IOException { delegate.handleError(response); } @Override public void handleError(ClientHttpResponse response, HttpStatus statusCode) throws IOException { delegate.handleError(response, statusCode); } public ClientHttpResponse cleanResponse(ClientHttpResponse response) throws IOException { if (HttpStatus.OK.equals(response.getStatusCode())) { String body = new BufferedReader(new InputStreamReader(response.getBody(), StandardCharsets.UTF_8)) .lines() .reduce("", String::concat); // 替换web字段中带嵌套引号的内容 String cleanedBody = body.replaceAll("\"web\":\"\\\\\"(.*?)\\\\\"\"", "\"web\":\"$1\""); // 包装响应体,返回清理后的内容 return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return response.getStatusCode(); } @Override public int getRawStatusCode() throws IOException { return response.getRawStatusCode(); } @Override public String getStatusText() throws IOException { return response.getStatusText(); } @Override public void close() { response.close(); } @Override public ByteArrayInputStream getBody() throws IOException { return new ByteArrayInputStream(cleanedBody.getBytes(StandardCharsets.UTF_8)); } @Override public org.springframework.http.HttpHeaders getHeaders() { return response.getHeaders(); } }; } return response; } }
配置RestTemplate使用该处理器:
RestTemplate restTemplate = new RestTemplate(); restTemplate.setErrorHandler(new CleanWebUriResponseHandler(restTemplate.getErrorHandler()));
内容的提问来源于stack exchange,提问作者asem
相关产品推荐
相关产品推荐

