我们知道,在自定义类中,若想完成序列化必须要实现Serializable接口。
那么在实现后如何进行序列化呢?
一.普通对象
序列化:
1.首先我们要定义一个 序列化所需要的工具类 ObjectMapper
//定义序列化所需要的工具类 转化机器
ObjectMapper objectMapper = new ObjectMapper();
2.定义所要序列化的类 (在这里以我正在做的项目中的一个类为例)
//定义所要序列化的类 转化原料
CommonResult<String> result = CommonResult.error(500, "系统错误");
3.调用objectMapper.writeValueAsString()方法序列化为String
//定义序列化后的类 转化产品
String str = null;
//序列化
try {
str = objectMapper.writeValueAsString(result);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
这个方法只有一个参数,就是要序列化对象本身
打印结果
反序列化:
//反序列化 JsonParser:反序列化所转化的对象 ResolvedType: 所返回的对象的类型
try {
CommonResult<String> commonResult1 = objectMapper.readValue(str
, CommonResult.class);
System.out.println(commonResult1);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
这个方法有两个参数
JsonParser:反序列化所转化的对象(str 刚序列化后的产物)
ResolvedType: 所返回的对象的类型(CommonResult)
返回值就是原本对象
打印结果:
二.List对象
序列化:
1.定义ObjectMapper (上文已经定义过)
2.初始化所要序列化的List(用于演示)
3.调用objectMapper.writeValueAsString()可见与普通对象无差别
真正的区别在反序列化
//序列化 List
List<CommonResult<String>> list = Arrays.asList(
CommonResult.success("success1"),
CommonResult.success("success2")
);
try {
str = objectMapper.writeValueAsString(list);
System.out.println(str);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
反序列化:
因为我们不能保证在每个List中存放的类型一致,所以在objectMapper.readValue()方法的第二个参数(类型)不能保持一致,这里引入一个新参数JavaType,也就是反序列化调用的方法参数
看名字就知道它是用于描述java中类型的,我们看看他怎么用的。
JavaType javaType = objectMapper.getTypeFactory()
.constructParametricType(List.class, CommonResult.class);
1.调用objectMapper的类型工厂objectMapper.getTypeFactory()(充当工具)
2.获取JavaType constructParametricType()
这个方法有多个重载,这里有两个参数:
第一个不会变,要序列化的对象类型(List)
第二个:List中的属性类型(CommonResult)
在Map中由于Map有两个参数所以会有第三个参数(按顺序写即可)
然后调用objectMapper.readValue()即可
List<CommonResult<String>> result2 = null;
try {
result2 = objectMapper.readValue(str, javaType);
// System.out.println(result2);
for(CommonResult<String> a : result2) {
System.out.println(a.getData());
}
} catch (JsonProcessingException e) {
e.printStackTrace();
}
三.Map对象
还是按照前文,注意constructParametricType()方法传入三个参数即可
//序列化 Map
Map<Integer, CommonResult<String>> map = new HashMap<>();
map.put(3,CommonResult.success("success3"));
map.put(4,CommonResult.success("success4"));
try {
str = objectMapper.writeValueAsString(map);
System.out.println(str);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//反序列化 先构造一个所需要的参数类型 Map类型 内部是什么类型
JavaType javaType2 = objectMapper.getTypeFactory()
.constructParametricType(Map.class, Integer.class, CommonResult.class);
Map<Integer, CommonResult<String>> result3 = null;
try {
result3 = objectMapper.readValue(str, javaType2);
// System.out.println(result2);
System.out.println(map.get(3).getCode());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
四.封装成方法
在上述写过程中try了许多的异常,非常的冗余,我们可以参考一下JacksonJsonParser源码对这个问题解决方法。
在源码部分对List与Map是这样实现的:
public Map<String, Object> parseMap(String json) {
return (Map)this.tryParse(() -> {
return (Map)this.getObjectMapper().readValue(json, MAP_TYPE);
}, Exception.class);
}
public List<Object> parseList(String json) {
return (List)this.tryParse(() -> {
return (List)this.getObjectMapper().readValue(json, LIST_TYPE);
}, Exception.class);
}
我们发现它并没有进行异常捕获,进入tryParse()方法看一下:
protected final <T> T tryParse(Callable<T> parser, Class<? extends Exception> check) {
try {
return parser.call();
} catch (Exception var4) {
if (check.isAssignableFrom(var4.getClass())) {
throw new JsonParseException(var4);
} else {
ReflectionUtils.rethrowRuntimeException(var4);
throw new IllegalStateException(var4);
}
}
}
原来是在这里进行了异常处理。通过在tryParse()方法传入lambda表达式然后再call()返回(这一步并没有进行实际业务执行,单纯为了将处理异常这一步统一处理)。再判断调用lambda时不活的异常是否是原业务的异常,如果不是就再爆其他的异常。
我们参照这个解决逻辑,完成一个在项目中使用的JacksonUtil类。
同时注意到在源码部分对ObjectMapper这个一直用的对象进行了单例模式的实现,我们也学习一下。
1.单例模式实现ObjectMapper
/**
* 单例模式实现ObjectMapper
*/
private static final ObjectMapper OBJECT_MAPPER;
static {
OBJECT_MAPPER = new ObjectMapper();
}
//构造方法私有化
private JacksonUtil() {
}
//获取OBJECT_MAPPER对象
public ObjectMapper getObjectMapper() {
return JacksonUtil.OBJECT_MAPPER;
}
2.解决try catch太多的方法
我们可以直接复制源码部分的tryParse代码
只需把final改为static(方便其他方法调用)
else判断的部分也可以不要(jdk自己对异常的补充,可以不要)
protected static <T> T tryParse(Callable<T> parser, Class<? extends Exception> check) {
try {
return parser.call();
} catch (Exception var4) {
if (check.isAssignableFrom(var4.getClass())) {
throw new JsonParseException(var4);
}
throw new IllegalStateException(var4);
}
}
我们可以对这个方法进行封装一下:
/**
* 封装tryParse方法 使其固定爆出JacksonException异常
* @param parser 传入的lambda表达式
* @param <T>
* @return
*/
private final <T> T tryParse(Callable<T> parser) {
return JacksonUtil.tryParse(parser, JacksonException.class);
}
3.实现序列化
由于普通对象与List Map的序列化过程几乎相同,所以我们一个方法就可以实现。
/**
* 序列化 对普通对象 与 List 都适用
* @param object
* @return
*/
public static String writeValueAsString(Object object) {
return JacksonUtil.tryParse(() -> {
return JacksonUtil.getObjectMapper().writeValueAsString(object);
});
}
4.实现反序列化
普通对象序列化:
/**
* 普通对象反序列化
* @param content
* @param valueType
* @param <T>
* @return
*/
public static <T> T readValue(String content, Class<T> valueType) {
return JacksonUtil.tryParse(() -> {
return JacksonUtil.getObjectMapper().readValue(content, valueType);
});
}
List反序列化:注意将第二个参数类型改为 Class<?>而不是 Class<T>
/**
* 反序列化 List
* @param content
* @param paramClasses
* @param <T>
* @return
*/
public static <T> T readListValue (String content, Class<?> paramClasses) {
JavaType javaType = JacksonUtil.getObjectMapper().getTypeFactory()
.constructParametricType(List.class, paramClasses);
return JacksonUtil.tryParse(() -> {
return JacksonUtil.getObjectMapper().readValue(content, javaType);
});
}
最后附上测试代码:
@Test
void jackUtilTest() {
CommonResult<String> result = CommonResult.error(500, "系统错误");
String str = JacksonUtil.writeValueAsString(result);
System.out.println(str);
CommonResult<String> commonResult = JacksonUtil.readValue(str, CommonResult.class);
System.out.println(commonResult);
//序列化 List
List<CommonResult<String>> list = Arrays.asList(
CommonResult.success("success1"),
CommonResult.success("success2")
);
String str1 = JacksonUtil.writeValueAsString(list);
System.out.println(str1);
list = JacksonUtil.readListValue(str1, CommonResult.class);
for(CommonResult<String> a : list) {
System.out.println(a.getData());
}
}