1、问题
最近有个需求,数据是保存在一个Map<String, Object>
中,使用Fastjson转为json字符串后保存到数据库,要用的时候从数据库取出来然后还原。
示例代码如下
package com.moss.json;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import com.alibaba.fastjson.JSON;
import com.moss.mysql.entity.AppUser;
public class JsonTest {
private static Map<String, Object> localMap = new HashMap<>();
public static void main(String[] args) {
AppUser user = new AppUser();
user.setUserId(System.currentTimeMillis());
user.setLoginIp("127.0.0.1");
user.setUserName("管理员");
user.setEmail("admin@126.com");
user.setNickName("moss");
user.setDelFlag("0");
user.setDeptId(System.currentTimeMillis());
user.setSex("0");
user.setLoginDate(new Timestamp(System.currentTimeMillis()));
user.setPassword("123456");
user.setPhonenumber("13522659887");
Map<String, Object> extMap = new HashMap<>();
extMap.put("serialNo", UUID.randomUUID().toString());
extMap.put("user", user);
extMap.put("total", 100);
// 序列化
String json = JSON.toJSONString(extMap);
System.out.println("Json = " + json);
// 反序列化
localMap = JSON.parseObject(json);
for (Map.Entry<String, Object> e : localMap.entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue() + ", type = " + e.getValue().getClass());
}
AppUser appUser = get("user");
System.out.println("appUser = " + appUser);
}
public static <T> T get(String key) {
return (T) localMap.get(key);
}
}
运行结果
Json = {"total":100,"user":{"delFlag":"0","deptId":1625713027348,"email":"admin@126.com","loginDate":1625713027349,"loginIp":"127.0.0.1","nickName":"moss","password":"123456","phonenumber":"13522659887","sex":"0","userId":1625713027348,"userName":"管理员"},"serialNo":"c82f8649-78ad-4758-9a13-7b5613d24e5a"}
total = 100, type = class java.lang.Integer
user = {"password":"123456","nickName":"moss","loginIp":"127.0.0.1","sex":"0","deptId":1625713027348,"phonenumber":"13522659887","loginDate":1625713027349,"delFlag":"0","userName":"管理员","userId":1625713027348,"email":"admin@126.com"}, type = class com.alibaba.fastjson.JSONObject
serialNo = c82f8649-78ad-4758-9a13-7b5613d24e5a, type = class java.lang.String
Exception in thread "main" java.lang.ClassCastException: com.alibaba.fastjson.JSONObject cannot be cast to com.moss.mysql.entity.AppUser
at com.moss.json.JsonTest.main(JsonTest.java:41)
结果分析
从上面可以看到AppUser
对象在转为JSON字符串后的类型变成了com.alibaba.fastjson.JSONObject
对象,在最后取出来就发生了ClassCastException
异常了
怎么办呢。。。。。。。。。。。。。。
网上找了一堆,并没有找到解决方法,最后找了官方文档,发现了可以这样搞
// 其它不变,这里序列化时,加上SerializerFeature.WriteClassName,保留原始类型
String json = JSON.toJSONString(extMap, SerializerFeature.WriteClassName);
System.out.println("Json = " + json);
// 反序列化时加上TypeReference转成原始类型
localMap = JSON.parseObject(json, new TypeReference<Map<String, Object>>(){});
再次运行,发现转换后的json带上了原始类型了,如"@type":“com.moss.mysql.entity.AppUser”,但是。。。com.alibaba.fastjson.JSONException: autoType is not support又是什么鬼,还是不行,继续挖。。。
Json = {"@type":"java.util.HashMap","total":100,"user":{"@type":"com.moss.mysql.entity.AppUser","delFlag":"0","deptId":1625713550766,"email":"admin@126.com","loginDate":1625713550766,"loginIp":"127.0.0.1","nickName":"moss","password":"123456","phonenumber":"13522659887","sex":"0","userId":1625713550765,"userName":"管理员"},"serialNo":"df414adf-35e3-49dd-bbf7-b2b7ae6b962d"}
Exception in thread "main" com.alibaba.fastjson.JSONException: autoType is not support. com.moss.mysql.entity.AppUser
at com.alibaba.fastjson.parser.ParserConfig.checkAutoType(ParserConfig.java:1466)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:333)
at com.alibaba.fastjson.parser.DefaultJSONParser.parse(DefaultJSONParser.java:1401)
at com.alibaba.fastjson.parser.deserializer.JavaObjectDeserializer.deserialze(JavaObjectDeserializer.java:46)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:688)
at com.alibaba.fastjson.parser.deserializer.MapDeserializer.parseMap(MapDeserializer.java:198)
at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:65)
at com.alibaba.fastjson.parser.deserializer.MapDeserializer.deserialze(MapDeserializer.java:43)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:688)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:396)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:364)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:278)
at com.moss.json.JsonTest.main(JsonTest.java:38)
Process finished with exit code 1
继续找了一遍,发现需要打开autoType功能,开启的方法有2种
// 1、JVM启动参数
-Dfastjson.parser.autoTypeSupport=true
// 2、代码中设置
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
2、最终解决代码
官方文档:https://github.com/alibaba/fastjson/wiki/enable_autotype
package com.moss.json;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.moss.mysql.entity.AppUser;
public class JsonTest {
private static Map<String, Object> localMap = new HashMap<>();
// 1、开启AutoTypeSupport
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
}
public static void main(String[] args) {
AppUser user = new AppUser();
user.setUserId(System.currentTimeMillis());
user.setLoginIp("127.0.0.1");
user.setUserName("管理员");
user.setEmail("admin@126.com");
user.setNickName("moss");
user.setDelFlag("0");
user.setDeptId(System.currentTimeMillis());
user.setSex("0");
user.setLoginDate(new Timestamp(System.currentTimeMillis()));
user.setPassword("123456");
user.setPhonenumber("13522659887");
Map<String, Object> extMap = new HashMap<>();
extMap.put("serialNo", UUID.randomUUID().toString());
extMap.put("user", user);
extMap.put("total", 100);
// 2、序列化为JSON时,增加SerializerFeature.WriteClassName,保留数据原始类型
String json = toJson(extMap);
System.out.println("Json = " + json);
// 3、反序列化时,用TypeReference指定原始类型
localMap = toObject(json);
for (Map.Entry<String, Object> e : localMap.entrySet()) {
System.out.println(e.getKey() + " = " + e.getValue() + ", type = " + e.getValue().getClass());
}
AppUser appUser = get("user");
System.out.println("appUser = " + appUser);
}
public static <T> T get(String key) {
return (T) localMap.get(key);
}
/**
* 转换为json
* @param value
* @return
*/
public static String toJson(Object value) {
if (Objects.isNull(value)) {
return null;
}
return JSON.toJSONString(value, SerializerFeature.WriteClassName);
}
/**
* 转换成java Object
* @param jsonValue
* @param <T>
* @return
*/
public static <T> T toObject(String jsonValue) {
if (StringUtils.isEmpty(jsonValue)) {
return null;
}
return JSON.parseObject(jsonValue, new TypeReference<T>(){});
}
}
执行结果
Json = {"@type":"java.util.HashMap","total":100,"user":{"@type":"com.moss.mysql.entity.AppUser","delFlag":"0","deptId":1625714298279,"email":"admin@126.com","loginDate":1625714298279,"loginIp":"127.0.0.1","nickName":"moss","password":"123456","phonenumber":"13522659887","sex":"0","userId":1625714298279,"userName":"管理员"},"serialNo":"1e657959-2299-432d-82ed-87e7357e40e9"}
total = 100, type = class java.lang.Integer
user = AppUser{userId=1625714298279, deptId=1625714298279, userName=管理员, nickName=moss, userType=null, email=admin@126.com, phonenumber=13522659887, sex=0, avatar=null, password=123456, status=null, delFlag=0, loginIp=127.0.0.1, loginDate=2021-07-08 11:18:18.279, createBy=null, createTime=null, updateBy=null, updateTime=null, remark=null}, type = class com.moss.mysql.entity.AppUser
serialNo = 1e657959-2299-432d-82ed-87e7357e40e9, type = class java.lang.String
appUser = AppUser{userId=1625714298279, deptId=1625714298279, userName=管理员, nickName=moss, userType=null, email=admin@126.com, phonenumber=13522659887, sex=0, avatar=null, password=123456, status=null, delFlag=0, loginIp=127.0.0.1, loginDate=2021-07-08 11:18:18.279, createBy=null, createTime=null, updateBy=null, updateTime=null, remark=null}
Process finished with exit code 0
3、课外知识
添加autotype白名单,有三种方式
// 1、在代码中配置,如果有多个包名前缀,分多次addAccept
ParserConfig.getGlobalInstance().addAccept("com.moss.");
ParserConfig.getGlobalInstance().addAccept("com.husun.");
// 2、加上JVM启动参数,如果有多个包名前缀,用逗号隔开
-Dfastjson.parser.autoTypeAccept=com.moss.,com.husun.
// 3、通过类路径的fastjson.properties文件来配置,如果有多个包名前缀,用逗号隔开
fastjson.parser.autoTypeAccept=com.moss.,com.husun.
添加autotype黑名单,有三种方式
// 1、在代码中配置,如果有多个包名前缀,分多次addDeny
ParserConfig.getGlobalInstance().addDeny("com.moss.");
ParserConfig.getGlobalInstance().addDeny("com.husun.");
// 2、加上JVM启动参数,如果有多个包名前缀,用逗号隔开
-Dfastjson.parser.deny=com.moss.,com.husun.
// 3、通过类路径的fastjson.properties文件来配置,如果有多个包名前缀,用逗号隔开
fastjson.parser.deny=com.moss.,com.husun.