作为java开发者,我们常用的序列化方式主要有3种:jackson 、fastJson(fastJson2)、gson。但在使用场景上,他们有不同的偏向。
使用建议
a.Jackson的扩展性较强,且SpringBoot默认是使用Jackson作为JSON数据格式处理的类库。因此关于接口返回的处理,仍可使用jackson处理。
b.Gson的功能较为全面,且支持具有深度继承层次结构的复杂对象转换。如果只是功能要求,没有特别严格的性能要求,可以使用google的Gson;
c.fastJson的速度相对较快,可在高并发场景中使用。但在复杂类型的Bean转换Json上(如泛型嵌套)可能会出现一些问题,且与升级版本fastJson2在序列化格式上并不完全兼容。另外自身被爆安全漏洞较多
注意事项
1.不要使用hutool封装的JsonUtil工具,性能较差;
2.同一个业务逻辑中,最好使用同一种序列化方式。不要混用json序列化的方式。
如下代码出现fastJson和Gson混用,不建议使用:
JSONObject.parseObject(new Gson().toJson(jsonObject),UserDTO.class)
3.日期格式的处理要谨慎。不同序列化方式,Date类型的字段序列化的格式不一样,fastJson和jackson都是序列化成为时间戳,但gson是日期格式。因此针对跨服务场景下的日期字段,需在约定统一格式进行处理。
比如针对全局的springMVC处理,可以做如下配置,则所有接口的Date类型字段都会按照统一格式返回。
spring.jackson.date-format= yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone= GMT+8
4.非驼峰书写格式字段处理要注意。
在项目定义的java bean 中,针对属性是首字母有大写的特殊情况,不同序列化方式的处理不同。最好加上@JsonProperty 注解或Fastjson 的@JSONField注解。
比如针对如下的bean USERPwd和UserAccount 都是首字母大写:
@Data
public class UserDTO {
private String userName;
// @JsonProperty(value ="USERPwd")
private String USERPwd;
// @JsonProperty(value ="UserAccount")
private String UserAccount;
private String eName;
private Integer age;
private Date time;
}
在没有任何注解的情况下,分别使用jackson、fastJson、gson进行序列化,区别如下:
jackson: {"userName":"test","age":20,"ename":"hello","userpwd":"abc123","UserAccount":"Liu8888"}
fastJson: {"age":20,"eName":"hello","uSERPwd":"abc123","userAccount":"Liu8888","userName":"test"}
fastJson2: {"EName":"hello","USERPwd":"abc123","age":20,"userAccount":"Liu8888","userName":"test"}
gson: {"userName":"test","USERPwd":"abc123","UserAccount":"Liu8888","eName":"hello","age":20}
5.注意泛型类型擦除问题
以上述的UserDTO 为例,假设对List<UserDTO> 一个集合信息做序列化后,如果直接进行反序列化,则会因为泛型信息缺失,导致反序列化后的集合元素并非UserDTO类型(fastJson是JSONObject,gson对应的是LinkedTreeMap).
此时需借助如下方式处理:fastJson采用TypeReference 处理;gson采用TypeToken 方式处理。
6.JDK8的LocalDateTime类型转换时要注意
Jackson默认的ObjectMapper不支持,需要加入如下配置才可以:
objectMapper.registerModule(new JavaTimeModule());
附录:
这里给出了基于Gson的工具类封装,大家可以直接使用:
public class JsonUtil {
/**
* 定义全局GsonBuilder对象
*/
private static final Gson gson =new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
private JsonUtil(){
}
public static Gson getInstance(){
return gson;
}
public static String toJson(Object src) {
return gson.toJson(src);
}
public static String toJson(Object src, Type typeOfSrc) {
return gson.toJson(src,typeOfSrc);
}
/**
* 根据class类型解析json
* @param json
* @param classOfT
* @param <T>
* @return
*/
public static <T> T fromJson(String json, Class<T> classOfT) {
return gson.fromJson(json,classOfT);
}
/**
* 根据type解析json
* @param json
* @param typeOfT
* @param <T>
* @return
*/
public static <T> T fromJson(String json, Type typeOfT) {
return gson.fromJson(json,typeOfT);
}
/**
* json 转成 特定的cls的list
* @param json
* @param classOfT
* @param <T>
* @return
*/
public static <T> List<T> fromJsonList(String json, Class<T> classOfT) {
// return gson.fromJson(json,new TypeToken<List<T>>() {}.getType());
Type typeOfT = TypeToken.getParameterized(List.class, classOfT).getType();
return gson.fromJson(json, typeOfT);
}
/**
* json 转成 特定的 rawClass<classOfT> 的Object
*
* @param json
* @param classOfT
* @param argClassOfT
* @return
*/
public static <T> T fromJson(String json, Class<T> classOfT, Class argClassOfT) {
Type type = new GsonUtil.ParameterizedType4ReturnT(classOfT, new Class[]{argClassOfT});
return gson.fromJson(json, type);
}
public static class ParameterizedType4ReturnT implements ParameterizedType {
private final Class raw;
private final Type[] args;
public ParameterizedType4ReturnT(Class raw, Type[] args) {
this.raw = raw;
this.args = args != null ? args : new Type[0];
}
@Override
public Type[] getActualTypeArguments() {
return args;
}
@Override
public Type getRawType() {
return raw;
}
@Override
public Type getOwnerType() {return null;}
}
}
注意:GsonBuilder这里指定了统一的日期格式为:"yyyy-MM-dd HH:mm:ss"。如果大家在项目中需要使用其他日期格式,请替换此格式即可。