一、背景
有一个http查询接口,返回json报文,然后反序列为一个ApiResult对象,最后取出对象的data字段内容,并反序列为ClassroomCopyResultNotify对象。
但是,在最后反序列化的时候,报错如下:
java.lang.IllegalArgumentException: Cannot construct instance of `com.xxx.service.dto.ClassroomCopyResultNotify`
(although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value
('{"requestNo":"658ac2929d47479fbbe5a4a7ec86c451","newClassroomId":"FJGYYISL","sourceClassroomId":"XOJG0AOB"}')at [Source: UNKNOWN; line: -1, column: -1]
二、伪代码
下面贴出相关的类代码:
1、ApiResult.java
@ApiModel
@Getter
public final class ApiResult<T> {@ApiModelProperty(notes = "code")private int code;@ApiModelProperty(notes = "报文体")private T data;@ApiModelProperty(notes = "msg")private String msg;
}
2、Json报文反序列化为ApiResult对象
- json报文:
{"code": 200,"msg": "success","data": "{\"requestNo\":\"658ac2929d47479fbbe5a4a7ec86c451\",\"newClassroomId\":\"FJGYYISL\",\"sourceClassroomId\":\"XOJG0AOB\"}"
}
- 反序列化,使用jackson框架的ObjectMapper
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;private static ObjectMapper objectMapper;static {objectMapper = new ObjectMapper();//忽略反序列化中的一些错误objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);}ApiResult<?> apiResult = objectMapper.readValue(jsonStr, new TypeReference<ApiResult<?>>() {});
3、取出apiResult的data内容,反序列化为java对象
下面的代码是出错时的写法。
ClassroomCopyResultNotify response = objectMapper.convertValue(apiResult.getData(), ClassroomCopyResultNotify.class);
错误在于,data字段的内容,并不是一个严格意义的json字符串。它多了一个反斜杠。
详细的报错内容见下:
java.lang.IllegalArgumentException: Cannot construct instance of `com.xxx.service.dto.ClassroomCopyResultNotify` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"requestNo":"658ac2929d47479fbbe5a4a7ec86c451","newClassroomId":"FJGYYISL","sourceClassroomId":"XOJG0AOB"}')at [Source: UNKNOWN; line: -1, column: -1]at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3922)at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:3853)at java.lang.Iterable.forEach(Iterable.java:75)at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:366)at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:99)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)at sun.reflect.GeneratedMethodAccessor358.invoke(Unknown Source)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:498)at com.xxl.job.core.handler.impl.MethodJobHandler.execute(MethodJobHandler.java:29)at com.xxl.job.core.thread.JobThread.run(JobThread.java:152)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.xxx.service.dto.ClassroomCopyResultNotify` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"requestNo":"658ac2929d47479fbbe5a4a7ec86c451","newClassroomId":"FJGYYISL","sourceClassroomId":"XOJG0AOB"}')at [Source: UNKNOWN; line: -1, column: -1]at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1429)at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1059)at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3917)... 25 common frames omitted
- 期望data字段的内容见下:
{"code": 200,"msg": "success","data": {"requestNo": "658ac2929d47479fbbe5a4a7ec86c451","newClassroomId": "FJGYYISL","sourceClassroomId": "XOJG0AOB"}
}
注意: 因为Http响应报文在经过jackjson序列化的时候,data就是一个字符串,无法去掉反斜杠。
4、正确的写法应该是:
ClassroomCopyResultNotify response = JSON.parseObject(apiResult.getData().toString(), ClassroomCopyResultNotify.class);
三、完整的代码
- ClassroomCopyResultNotify.java
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ClassroomCopyResultNotify {private String sourceClassroomId;private String newClassroomId;private String requestNo;
}
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xhtech.service.course.interfaces.http.assembler.ApiResult;public class JsonUtils {private static ObjectMapper objectMapper;static {objectMapper = new ObjectMapper();//忽略反序列化中的一些错误objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);}public static void main(String[] args) {ApiResult<?> apiResult = getApiResult();if (apiResult != null) {try {ClassroomCopyResultNotify response = JSON.parseObject(apiResult.getData().toString(), ClassroomCopyResultNotify.class);System.out.println("json内容反序列化后(正确的写法):" +response);} catch (Exception e) {e.printStackTrace();}try {ClassroomCopyResultNotify response = objectMapper.convertValue(apiResult.getData(), ClassroomCopyResultNotify.class);System.out.println("json内容反序列化后(错误的写法):" +response);} catch (Exception e) {e.printStackTrace();}}ApiResult<?> apiResultForExpected = getApiResultForExpected();if (apiResultForExpected != null) {try {ClassroomCopyResultNotify response = JSON.parseObject(apiResult.getData().toString(), ClassroomCopyResultNotify.class);System.out.println("期望的json内容反序列化后(fastjson的写法):" +response);} catch (Exception e) {e.printStackTrace();}try {ClassroomCopyResultNotify response = objectMapper.convertValue(apiResultForExpected.getData(), ClassroomCopyResultNotify.class);System.out.println("期望的json内容反序列化后(jackson的写法):" + response);} catch (Exception e) {e.printStackTrace();}}}private static ApiResult<?> getApiResult() {try {String jsonStr = "{\n" +" \"code\": 200,\n" +" \"msg\": \"success\",\n" +" \"data\": \"{\\\"requestNo\\\":\\\"658ac2929d47479fbbe5a4a7ec86c451\\\",\\\"newClassroomId\\\":\\\"FJGYYISL\\\",\\\"sourceClassroomId\\\":\\\"XOJG0AOB\\\"}\"\n" +"}";ApiResult<?> apiResult = objectMapper.readValue(jsonStr, new TypeReference<ApiResult<?>>() {});return apiResult;} catch (Exception e) {return null;}}private static ApiResult<?> getApiResultForExpected() {try {String expectedJsonStr = "{\n" +" \"data\": {\n" +" \"requestNo\": \"658ac2929d47479fbbe5a4a7ec86c451\",\n" +" \"newClassroomId\": \"FJGYYISL\",\n" +" \"sourceClassroomId\": \"XOJG0AOB\"\n" +" },\n" +" \"code\": 200,\n" +" \"msg\": \"success\"\n" +"}";ApiResult<?> apiResult = objectMapper.readValue(expectedJsonStr, new TypeReference<ApiResult<?>>() {});return apiResult;} catch (Exception e) {return null;}}}
- 运行结果
json内容反序列化后(正确的写法):ClassroomCopyResultNotify(sourceClassroomId=XOJG0AOB, newClassroomId=FJGYYISL, requestNo=658ac2929d47479fbbe5a4a7ec86c451)java.lang.IllegalArgumentException: Cannot construct instance of `com.xxx.service.dto.ClassroomCopyResultNotify` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"requestNo":"658ac2929d47479fbbe5a4a7ec86c451","newClassroomId":"FJGYYISL","sourceClassroomId":"XOJG0AOB"}')at [Source: UNKNOWN; line: -1, column: -1]at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3922)at com.fasterxml.jackson.databind.ObjectMapper.convertValue(ObjectMapper.java:3853)at JsonUtils.main(JsonUtils.java:31)
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.xxx.service.dto.ClassroomCopyResultNotify` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"requestNo":"658ac2929d47479fbbe5a4a7ec86c451","newClassroomId":"FJGYYISL","sourceClassroomId":"XOJG0AOB"}')at [Source: UNKNOWN; line: -1, column: -1]at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1429)at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1059)at com.fasterxml.jackson.databind.deser.ValueInstantiator._createFromStringFallbacks(ValueInstantiator.java:371)at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromString(StdValueInstantiator.java:323)at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromString(BeanDeserializerBase.java:1373)at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeOther(BeanDeserializer.java:171)at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:161)at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:3917)... 2 more期望的json内容反序列化后(fastjson的写法):ClassroomCopyResultNotify(sourceClassroomId=XOJG0AOB, newClassroomId=FJGYYISL, requestNo=658ac2929d47479fbbe5a4a7ec86c451)期望的json内容反序列化后(jackson的写法):ClassroomCopyResultNotify(sourceClassroomId=XOJG0AOB, newClassroomId=FJGYYISL, requestNo=658ac2929d47479fbbe5a4a7ec86c451)
四、总结
通过上面代码的运行结果可以得知,fastjson在处理带反斜杠的json字符串时,兼容性比jackson要强。