需要背景
需要一:SpringMVC构建的微服务零碎,数据库对日期的存储是Long类型的工夫戳,前端之前是默认应用Long类型工夫,当初前端框架改变,要求后端响应数据时,Long类型的工夫主动变成规范工夫格局(yyyy-MM-dd HH:mm:ss)。
波及到这个转换的范畴挺大,所有的实体表都有创立工夫createTime和批改工夫updateTime,目前的次要诉求也是针对这两个字段,并且在实体详情数据和列表数据都存在,须要一个对立的办法,对这两个字段进行解决。
需要二:前端申请上传的JSON报文,String类型的内容,可能会呈现前后有空格的景象,如果前端框架未对此问题进行解决,后端收到的JSON申请反序列化为对象时,就会呈现String类型的值,前后有空格,现须要一个对立的解决办法,对接管的String类型属性执行trim办法。
解决方案
SpringMVC默认的JSON框架为jackson,也能够应用fastjson。
jackson框架
自定义序列化
如果我的项目应用jackson框架做json序列化,举荐的计划是应用@JsonSerialize注解,示例代码如下:
@JsonSerialize(using = CustomDateSerializer.class)
private Long createTime;
@JsonSerialize(using = CustomDateSerializer.class)
private Long updateTime;
CustomDateSerializer类的实现示例如下:
public class CustomDateSerializer extends JsonSerializer<Long> {
@Override
public void serialize(Long aLong, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(aLong);
jsonGenerator.writeString(sdf.format(date));
}
}
这种计划的益处如下:
- 自定义的实现类能够复用
- 精准到须要转换解决的字段,不受限于createTime和updateTime,更贴近于需要
毛病就是须要转换的字段都须要应用注解,工作量有点大
当然有其余的对立解决计划,这里不赘述。
自定义反序列化
在jackson框架上实现自定义序列化,也是十分不便的,继承SimpleModule类即可:
@Component
public class StringTrimModule extends SimpleModule {
public StringTrimModule() {
addDeserializer(String.class, new StdScalarDeserializer<String>(String.class) {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext ctx) throws IOException {
String value = jsonParser.getValueAsString();
if (StringUtils.isEmpty(value)) {
return value;
}
return value.trim();
}
});
}
}
fastjson框架
如果工程里呈现这个依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
阐明此工程应用的json框架为fastjson,那么jackson的@JsonSerialize就不会有触发入口了,咱们来看看fastjson的解决形式。
自定义序列化
相应的,应用fastjson会有相应的配置类,示例如下:
/**
* 对立输入是采纳fastJson
*
* @return
*/
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
//convert转换音讯的对象
FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
//解决中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
fastConverter.setSupportedMediaTypes(fastMediaTypes);
//是否要格式化返回的json数据
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
// 增加指定字段的值转换解决
fastJsonConfig.setSerializeFilters(new CustomerDateFilter());
// FastJson禁用autoTypeSupport
fastJsonConfig.getParserConfig().setAutoTypeSupport(false);
fastConverter.setFastJsonConfig(fastJsonConfig);
return new HttpMessageConverters(fastConverter);
}
这里须要增加fastjson对字段值的解决(上述代码已增加这行代码),如
// 增加指定字段的值转换解决
fastJsonConfig.setSerializeFilters(new CustomerDateFilter());
CustomerDateFilter为自行实现的类,代码如下:
public class CustomerDateFilter implements ValueFilter {
@Override
public Object process(Object object, String name, Object value) {
if (FieldConstants.CREATE_TIME.equalsIgnoreCase(name) || FieldConstants.UPDATE_TIME.equalsIgnoreCase(name)) {
// 属性名为createTime, updateTime进行转换解决
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
if(value instanceof Long) {
Long time = (Long) value;
Date date = new Date(time);
return sdf.format(date);
} else {
return value;
}
}
return value;
}
}
这样就能够把所有响应对象中呈现的createTime和updateTime字段对立解决了,无论列表数据还是单个对象数据,十分不便。毛病就是除此之外的字段,如果还做不到全零碎对立,就须要独自解决。
SerializeFilter定制序列化
反对SerializeFilter定制序列化的扩大编程接口有以下几个,可依据理论须要进行扩大:
- PropertyPreFilter: 依据PropertyName判断是否序列化;
- PropertyFilter: 依据PropertyName和PropertyValue来判断是否序列化;
- NameFilter: 批改Key,如果须要批改Key,process返回值则可;
- ValueFilter: 批改Value;
- BeforeFilter: 序列化时在最前增加内容;
- AfterFilter: 序列化时在最初增加内容;
自定义反序列化
fastJson提供了序列化过滤器,来实现自定义序列化革新,但没有提供反序列化过滤器,来实现对应的性能。
计划:@JSONField注解
回到对JSON报文String类型的值执行trim操作,官网反对@JSONField注解的属性设置(要求fastJson版本1.2.36以上):
@JSONField(format="trim")
private String name;
在JSON报文反序列化时,该实体的name属性会主动执行trim办法进行解决。
此计划只有一一增加注解,工作量较大。
计划:实现ObjectDeserializer接口
ObjectDeserializer接口为能够实现自定义反序列化实现接口,配合ParserConfig的全局设置,也能够达到预期的成果,合建StringTrimDeserializer类,对String进行解决:
/**
* @title: StringTrimDeserializer
* @description: 把String类型的内容对立做trim操作
*/
public class StringTrimDeserializer implements ObjectDeserializer {
@Override
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) {
// JSON String反序列化的逻辑比较复杂,在StringCodec的根底上,对其后果调用trim办法
Object obj = StringCodec.instance.deserialze(parser, type, fieldName);
if (obj instanceof String) {
String str = (String) obj;
return (T) str.trim();
}
return (T) obj;
}
@Override
public int getFastMatchToken() {
return JSONToken.LITERAL_STRING;
}
}
相应在,在HttpMessageConverters
类fastJsonHttpMessageConverters办法内中减少String类的反序列化设置:
// 设置String类的全局反序列化规定:主动实现trim操作
ParserConfig.getGlobalInstance().putDeserializer(String.class, new StringTrimDeserializer());
tips:
在StringTrimDeserializer类实现办法中为什么不间接parser.getLexer().stringVal()
失去值后执行trim办法,而是调用StringCodec.instance的实现办法?
StringCodec是fastJson默认的String类型的反序列化逻辑类,外面要解决的类型有String、StringBuffer、StringBuilder等,还有各种的汇合、数组构造,波及的nextToken值都不雷同,总之,对String文本的反序列化,实现逻辑和应答的场景都比较复杂,而此次的需要只是对String执行trim操作,简单的逻辑还是交给StringCodec来解决,站在StringCodec的根底上,对其后果执行trim办法就能够达到预期指标。
小结
明天这篇是记录Json自定义序列化和反序列化的实际计划,开始施行前先确认工程里应用的框架是哪个,否则就会呈现增加了@JsonSerialize注解,搞了大半天没有成果,回头一看框架是fastjson,没有触发入口,当然得不到预期成果,小小倡议,心愿对你有帮忙。
发表回复