全栈之路-小程序API-Json数据类型的序列化与反序列化

MySQL中有一种新的数据结构,就是json格式,在使用springboot中进行数据的读取的时候往往会将json类型的数据直接转换成字符串类型的数据,对于页面的数据的处理很不友好,如何来将json类型的数据序列化成List或者Map类型的,七月老师提供了一整套的代码完成以及思考过程,记录一下整个代码的优化过程

一、为什么要对这个进行优化?

1、这块主要涉及的是sku表中的specs字段,这个字段的意义在于可以准确的确定是哪一件商品,比如:我需要的是(颜色:暗夜绿色,版本:256G全网通)的iPhone 11 Pro手机,其中暗夜绿色,全网通的iPhone 11 Pro手机就是一条sku数据,暗夜绿色,全网通版本就是specs字段所存储的内容,我们在数据库中是以json字段进行存储的,我们不做处理的话,返回给前端的就是json字符串,

例如:"specs": "[{\"key\": \"颜色\", \"value\": \"橘黄色\", \"key_id\": 1, \"value_id\": 44}, {\"key\": \"图案\", \"value\": \"七龙珠\", \"key_id\": 3, \"value_id\": 9}, {\"key\": \"尺码\", \"value\": \"小号 S\", \"key_id\": 4, \"value_id\": 14}]"

虽然前端也是可以处理的,但是后台我们进行序列化之后,再返回给前端不是更加友好吗?哈哈,真是贴心的后端开发者!!!

2、最完美的返回形式

"specs": [{"key_id": 1,"key": "颜色","value_id": 45, "value": "金属灰" },{"key_id": 3, "key": "图案","value_id": 9,"value": "七龙珠"},{"key_id": 4, "key": "尺码","value_id": 14,"value": "小号 S"}],

这样的话,前端开发人员可以直接读取遍历出想要的数据

二、单体Json对象的映射处理

1、首先做一个单体Json对象与Map的转换的工具类

注意:

(1)json序列化工具用的是jackson

(2)异常处理是抛出500错误,也就是服务器异常,做了一下异常处理的封装(这里是把必须要处理的IO异常。转换成RuntimeException)

(3)一定要加上@Converter注解

 1 @Converter
 2 public class MapAndJson implements AttributeConverter<Map<String, Object>, String> {
 3 
 4     @Autowired
 5     private ObjectMapper mapper;
 6 
 7     @Override
 8     public String convertToDatabaseColumn(Map<String, Object> stringObjectMap) {
 9         try {
10             return mapper.writeValueAsString(stringObjectMap);
11         } catch (JsonProcessingException e) {
12             e.printStackTrace();
13             throw new ServerErrorException(9999);
14         }
15     }
16 
17     @Override
18     public Map<String, Object> convertToEntityAttribute(String s) {
19         try {
20             if (s == null) {
21                 return null;
22             }
23             return mapper.readValue(s, HashMap.class);
24         } catch (JsonProcessingException e) {
25             e.printStackTrace();
26             throw new ServerErrorException(9999);
27         }
28     }
29 }

2、转换字段中使用

1     @Convert(converter = MapAndJson.class)
2     private Map<String, Object> test;

三、数组类型json与List的映射

1、首先做一个数组类型Json与List转换的工具类

注意:这个工具类基本上和单体Json与Map映射很类似

 1 @Converter
 2 public class ListAndJson implements AttributeConverter<List<Object>, String> {
 3 
 4     @Autowired
 5     private ObjectMapper mapper;
 6 
 7     @Override
 8     public String convertToDatabaseColumn(List<Object> objects) {
 9         try {
10             return mapper.writeValueAsString(objects);
11         } catch (JsonProcessingException e) {
12             e.printStackTrace();
13             throw new ServerErrorException(9999);
14         }
15     }
16 
17     @Override
18     public List<Object> convertToEntityAttribute(String s) {
19         try {
20             if (s == null) {
21                 return null;
22             }
23             return mapper.readValue(s, List.class);
24         } catch (JsonProcessingException e) {
25             e.printStackTrace();
26             throw new ServerErrorException(9999);
27         }
28     }
29 
30 }

2、字段中使用

1     @Convert(converter = ListAndJson.class)
2     private List<Object> specs;

四、更优的解决方案

以上两个方法存在的问题是:没有办法确定具体的业务对象,就是一个Map,或者是List<Object>,没有办法确定到底是哪种业务对象,就像我们这里所要的,我们想要的是List<Spec>这种具体的Spec的对象的集合,这样的话,更加符合面向对象的思想,而且即使后面我们调用Spec具体对象中的业务方法的时候会更加方便,所以有必要做一个更加的优化方案!

1、创建一个通用的序列化反序列化工具类

注意:这里很不好理解的,只有对泛型比较熟悉,才能很容易理解,至于写出这种代码,我觉得目前还是写不出来的

 1 @Component
 2 public class GenericAndJson {
 3 
 4     private static ObjectMapper mapper;
 5 
 6     // 这里用到的set方法的自动注入,实例化static变量
 7     @Autowired
 8     public void setMapper(ObjectMapper mapper) {
 9         GenericAndJson.mapper = mapper;
10     }
11 
12     public static <T> String objectToJson(T o) {
13         try {
14             return GenericAndJson.mapper.writeValueAsString(o);
15         } catch (JsonProcessingException e) {
16             e.printStackTrace();
17             throw new ServerErrorException(9999);
18         }
19     }
20 
21     public static <T> T jsonToObject(String s, TypeReference<T> tr) {
22         try {
23             if (s == null) {
24                 return null;
25             }
26             return GenericAndJson.mapper.readValue(s, tr);
27         } catch (JsonProcessingException e) {
28             e.printStackTrace();
29             throw new ServerErrorException(9999);
30         }
31     }
32 
33 }

2、实际中使用

注意:需要重写变量的set与get方法,来进行序列化与反序列化操作

 1     private String specs;
 2 
 3     public List<Spec> getSpecs() {
 4         if(this.specs == null){
 5             return Collections.emptyList();
 6         }
 7         return GenericAndJson.jsonToObject(this.specs, new TypeReference<List<Spec>>() {
 8         });
 9     }
10 
11     public void setSpecs(List<Spec> specs) {
12         if(specs.isEmpty()){
13             return;
14         }
15         this.specs = GenericAndJson.objectToJson(specs);
16     }

内容出处:七月老师《从Java后端到全栈》视频课程

七月老师课程链接:https://class.imooc.com/sale/javafullstack