java8新出的YearMonth可以方便的用来表示某个月。我的项目中使用springmvc来接收YearMonth类型的数据时发现 x-www-from-urlencoded 格式的数据可以使用"2018-12"的类型接收,但是在post请求中 接收application/json的数据时出现以下错误
2020-02-18 11:18:25.284 WARN 16212 --- [nio-8090-exec-2] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: default constructor not found. class java.time.YearMonth; nested exception is com.alibaba.fastjson.JSONException: default constructor not found. class java.time.YearMonth;
通过异常应该是YearMonth没有公有构造函数 ,fastjson不支持解析YearMonth;案例使用fastjson作为了springmvc的序列化工具,类似如下的配置
@Configuration public class MyConverter implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); converters.add(0,fastJsonHttpMessageConverter); } }
通过查看MonthYear确实发现其私有化了构造函数,只留了两静态方法
private YearMonth(int year, int month) { this.year = year; this.month = month; } public static YearMonth of(int year, int month) { YEAR.checkValidValue(year); MONTH_OF_YEAR.checkValidValue(month); return new YearMonth(year, month); } private YearMonth with(int newYear, int newMonth) { if (year == newYear && month == newMonth) { return this; } return new YearMonth(newYear, newMonth); }
不过get请求时能获取得到,说明jackson应该是对其有专门的处理工具的。将springmvc的序列化工具修改为默认的jackson,发现能 将application/json请求中 “2018-12”格式的数据 顺利的反序列化YearMonth类型。然后通过查找发现jackson确实带有YearMonth的序列化反序列化工具
(看来springmvc将jackson作为默认序列化工具还是有原因的。。)
所以解决方法看来也找到了。就是使用默认的jackson作为序列化工具就可以解决了。
当然了,如果项目不好更改,想使用fastjson也有解决办法。fastjson允许为字段定制反序列化工具,然后对应字段标明使用指定的反序列化类即可 (序列化原理类似)
public class YearMonthDeserializer implements ObjectDeserializer { @Override public YearMonth deserialze(DefaultJSONParser parser, Type type, Object fieldName) { JSONLexer lexer = parser.getLexer(); String s = lexer.stringVal(); lexer.nextToken(16); return YearMonth.parse(s); } @Override public int getFastMatchToken() { return 0; } }
在需要反序列化的时候对需要的字段加上标注即可
public class AddVO { @JSONField(deserializeUsing = YearMonthDeserializer.class) private YearMonth yearMonth; public YearMonth getYearMonth() { return yearMonth; } public void setYearMonth(YearMonth yearMonth) { this.yearMonth = yearMonth; } }
当然也可以全局处理,在我们配置fastjson作为httpMessageConverter的地方代码改为如下即可
@Configuration public class MyConverter implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = fastJsonHttpMessageConverter.getFastJsonConfig(); fastJsonConfig.getParserConfig().putDeserializer( YearMonth.class,new YearMonthDeserializer()); converters.add(0,fastJsonHttpMessageConverter); } }