严重bug,序列化后不是标准的json格式,导致某些时刻反序列化报错
对象的属性为set集合时,序列化带类型,输出的并非json格式,导致某些时候无法解析。
直接上内容:下面这个set类型序列化之后,方括号前面有Set字符,并非标准的json格式。
public static void main(String[] args) {
TestA t = new TestA();
System.out.println(JSON.toJSONString(t, JSONWriter.Feature.WriteClassName, JSONWriter.Feature.PrettyFormat));
}
@Data
static class TestA {
Set<String> colSet = new HashSet<>();
List<String> colList = new ArrayList<>();
Map<String, String> colMap = new HashMap<>();
{
colSet.add("a");
colSet.add("b");
colList.add("a");
colList.add("b");
colMap.put("a", "b");
}
}
上面这个例子使用fastjson2进行反序列化是可以正常反序列化的,但是。。。。。。。。 如果使用此链接中的例子直接解析错误
再次附上源码:
@Test
void test() {
String jsonStr = """
{"@type":"top.jiangqiang.framework.system.entity.LoginUser","accountNonExpired":true,"accountNonLocked":true,"authorities":Set[{"@type":"org.springframework.security.core.authority.SimpleGrantedAuthority","authority":"123"},{"@type":"org.springframework.security.core.authority.SimpleGrantedAuthority","authority":"12312"}],"credentialsNonExpired":true,"enabled":true,"menuSet":Set[{"@type":"top.jiangqiang.framework.system.entity.vo.MenuVo","authority":"","component":"routerView/parent.vue","externalLink":false,"id":25,"menuSort":8,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isIframe":false,"isKeepAlive":true,"icon":"iconfont icon-diannao1","isAffix":false,"title":"测试名称","url":"","isHide":false},"name":"test","parentId":0,"path":"/test","redirect":""},{"@type":"top.jiangqiang.framework.system.entity.vo.MenuVo","component":"home/index.vue","externalLink":false,"id":1,"menuSort":1,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isLink":"","isIframe":false,"roles":["admin","common"],"isKeepAlive":true,"icon":"iconfont icon-shouye","isAffix":true,"title":"首页","isHide":false},"name":"home","parentId":0,"path":"/home"},{"@type":"top.jiangqiang.framework.system.entity.vo.MenuVo","authority":"","component":"3123123","externalLink":false,"id":51,"menuSort":0,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isIframe":false,"isKeepAlive":true,"icon":"","isAffix":false,"title":"123","url":"","isHide":false},"name":"3123","parentId":0,"path":"12312312","redirect":"1231"},{"@type":"top.jiangqiang.framework.system.entity.vo.MenuVo","authority":"123","component":"system/generator/index.vue","externalLink":false,"id":48,"menuSort":0,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isIframe":false,"isKeepAlive":true,"icon":"iconfont icon-diannao-shuju","isAffix":false,"title":"代码生成","url":"","isHide":false},"name":"112","parentId":0,"path":"/genCode","redirect":""},{"@type":"top.jiangqiang.framework.system.entity.vo.MenuVo","children":[{"component":"system/dept/index.vue","externalLink":false,"id":6,"menuSort":6,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isLink":"","isIframe":false,"roles":["admin"],"isKeepAlive":true,"icon":"ele-OfficeBuilding","isAffix":false,"title":"部门管理","isHide":false},"name":"systemDept","parentId":2,"path":"/system/dept"},{"component":"system/menu/index.vue","externalLink":false,"id":3,"menuSort":3,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isLink":"","isIframe":false,"roles":["admin"],"isKeepAlive":true,"icon":"iconfont icon-caidan","isAffix":false,"title":"菜单管理","isHide":false},"name":"systemMenu","parentId":2,"path":"/system/menu"},{"component":"system/user/index.vue","externalLink":false,"id":5,"menuSort":5,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isLink":"","isIframe":false,"roles":["admin"],"isKeepAlive":true,"icon":"iconfont icon-icon-","isAffix":false,"title":"用户管理","isHide":false},"name":"systemUser","parentId":2,"path":"/system/user"},{"component":"system/role/index.vue","externalLink":false,"id":4,"menuSort":4,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isLink":"","isIframe":false,"roles":["admin"],"isKeepAlive":true,"icon":"ele-ColdDrink","isAffix":false,"title":"角色管理","isHide":false},"name":"systemRole","parentId":2,"path":"/system/role"},{"authority":"","component":"system/i18n/index.vue","externalLink":false,"id":45,"menuSort":9,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isIframe":false,"isKeepAlive":true,"icon":"iconfont icon-diannao1","isAffix":false,"title":"国际化","url":"","isHide":false},"name":"i18n","parentId":2,"path":"/system/i18n","redirect":""},{"component":"system/dic/index.vue","externalLink":false,"id":7,"menuSort":7,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isLink":"","isIframe":false,"roles":["admin"],"isKeepAlive":true,"icon":"ele-SetUp","isAffix":false,"title":"字典管理","isHide":false},"name":"systemDic","parentId":2,"path":"/system/dic"}],"component":"routerView/parent.vue","externalLink":false,"id":2,"menuSort":2,"menuType":"menu","meta":{"@type":"java.util.LinkedHashMap","isLink":"","isIframe":false,"roles":["admin"],"isKeepAlive":true,"icon":"iconfont icon-xitongshezhi","isAffix":false,"title":"系统设置","isHide":false},"name":"system","parentId":0,"path":"/system","redirect":"/system/setting"}],"user":{"active":true,"deleted":false,"id":1,"nickname":"admin","password":"d29d20329fbd47d8b42fb2ac69069a4a104f215e509b85868c1f47ba9eab114dd6fdfe4d6812e7b7f498056f7b0ac5740c6c056e198632992fa8b9dd958e9bd5ef765b4e46b8fe58079eb6b10a7e563c4bee074b412138ca2d426a9b3f406ad3","username":"admin"}}
""";
Filter filter = JSONReader.autoTypeFilter(
// 这里可以配置多个前缀
"org.springframework.security.core.authority.SimpleGrantedAuthority",
"org.springframework.util.LinkedCaseInsensitiveMap"
);
JSON.mixIn(SimpleGrantedAuthority.class, SimMixin.class);
LoginUser loginUser = JSON.parseObject(jsonStr, LoginUser.class, filter);
Object o = redisTemplate.opsForValue().get("sys:user:info:1");
System.out.println(o);
}
static class SimMixin {
@JSONCreator
public SimMixin(@JSONField(value = true) String role) {
}
@JSONField(value = true)
public String getAuthority() {
return null;
}
}
LoginUser
@Data @NoArgsConstructor public class LoginUser implements UserDetails { private User user; /** * 帐户是否过期 / private boolean accountNonExpired = true; /* * 帐户是否被锁定 / private boolean accountNonLocked = true; /* * 密码是否过期 / private boolean credentialsNonExpired = true; /* * 帐户是否可用 / private boolean enabled = true; /* * 拥有权限集合 */ private Set<? extends GrantedAuthority> authorities;
/**
* 可访问的菜单集合
*/
private Set<MenuVo> menuSet;
public LoginUser(User user) {
this.user = user;
}
@JSONField(serialize = false,deserialize = false)
public String getPassword() {
return user.getPassword();
}
@JSONField(serialize = false,deserialize = false)
public String getUsername() {
return user.getUsername();
}
} @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @TableName("sys_user") public class User extends BaseEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* 用户名
*/
private String username;
/**
* 昵称
*/
private String nickname;
/**
* 姓名
*/
private String realName;
/**
* 密码
*/
private String password;
/**
* 头像
*/
private String avatar;
/**
* 性别 0:男 1:女 2:未知
*/
private Byte gender;
/**
* 启用/禁用
*/
private Boolean active;
/**
* 邮箱
*/
private String email;
/**
* 手机号
*/
private String mobile;
/**
* 用户简介
*/
private String resume;
}
@Data public class BaseEntity implements Serializable { @Serial @TableField(exist = false) private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 最后更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
/**
* 是否删除
*/
@TableLogic
private Boolean deleted;
}
下面是错误日志
我一开始还以为是我复制的json字符串有问题,然后我使用了redisTemplate直接获取,报错一样的。然后我特意使用了别的json解析工具,报错也是同一个地方。定位到解析错误的字符,就是set类型标识。
原提问者GitHub用户jiangqiang1996
使用WriteClassName之后,是要使用非标准的表示才能满足需求。
两个选择:
不使用WriteClassName 其他的场景也使用fastjson2 这个不是BUG,设计就是这样的
https://github.com/alibaba/fastjson2/releases/tag/2.0.14 问题已修复,请用新版本
原回答者GitHub用户wenshao
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。