侧边栏壁纸
  • 累计撰写 79 篇文章
  • 累计创建 84 个标签
  • 累计收到 7 条评论

目 录CONTENT

文章目录

Jackson序列化提示没有可以序列化的内容的问题分析

汤圆学Java
2022-02-17 / 0 评论 / 0 点赞 / 47 阅读 / 3,745 字
温馨提示:
本文最后更新于 2022-02-17,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

有时我们在序列化一个对象时,可能会遇到序列化失败的情况,报错提示没有发现可以序列化的内容,如下所示;

image-20220216110044126

下面我们就来分析一下报错的原因以及解决办法;

在分析之前,我们需要先了解一下Jackson的序列化机制;

目录

  1. Jackson的序列化机制
  2. 报错的原因分析
  3. 解决办法

正文

1. Jackson的序列化机制

Jackson序列化一个对象时,默认的序列化机制,就是从下面这两个地方进行提取数据,然后进行序列化处理:

  • 一个是对象中所有权限为public的字段;
  • 另一个是对象中所有权限为public的getter方法;

也就是说,如果一个对象中既没有public权限的字段,也没有public权限的getter方法,那么此时Jackson就无法提取对象中的数据,当然也就无法解析了;

此时不管对象有没有内容,对于外界来说都是不可见的;

2. 报错的原因分析

其实上面已经说明了原因,这里我们看个例子,先列出实体类User:

public class User {
    public User(String username, String nickname) {
        this.username = username;
        this.nickname = nickname;
    }

    public User() {
    }

    private String username;

    private String nickname;

}

可以看到,这里的实体类User我们只给了一个全参的构造函数 来进行初始化,而且所有字段都是private权限;

此时我们只能往里填充数据,却无法读取数据;

这种情况下,Jackson也是没有办法获取对象中的数据的,自然也就序列化失败了;

原因总结为一句话:因为实体类User没有public权限的field或者getter方法,导致Jackson找不到可以序列化的数据,结果就失败了

我们可以用例子来演示下:

User user = new User("jalon","xiaowang");
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(user);
System.out.println(s);

可以看到,我们给User对象填充了两个数据:jalon和xiaowang,但实际上运行却报错,显示如下:

image-20220216110044126

意思就是没有可以序列化的内容,导致序列化失败;

官方在这里给出了一个贴心的解决方案,就是在检测到待序列化的对象为空时,不要报错,而是输出空值;

但是这个解决方法有点掩耳盗铃的意思,因为我们明知道这个对象并不是空的,但是却输出空值,显然不符合预期;

对应的配置如下所示:

objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

下面我们就给出几个比较合理的解决办法;

3. 解决办法

办法1:实体类添加public权限的getter方法

这个办法是最简单的,直接为需要访问到的属性添加public的getter方法;

这样Jackson就可以成功读取了;

修改后的实体类如下:User.java

package com.jalon.jackson5;

public class User {
    public User(String username, String nickname) {
        this.username = username;
        this.nickname = nickname;
    }

    public User() {
    }

    private String username;

    private String nickname;

    public String getNickname() {
        return nickname;
    }

}

这里我们新增了public getNickname()这个getter方法,运行后如下所示:

image-20220216111109723

可以看到,序列化成功;

但是有时候实体类是不可编辑的,比如第三方的类,这时我们就需要用到下面的方法;

**办法2:类加注解 @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) **

望文生义,从字命意思来看,就是使得Jackson可以访问到实体类中所有权限的字段,包括private;

加了注解后的实体类User如下所示:

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class User {
    public User(String username, String nickname) {
        this.username = username;
        this.nickname = nickname;
    }

    public User() {
    }

    private String username;

    private String nickname;

}

序列化输出如下:

image-20220216111347993

可以看到,username和nickname自动都被成功序列化;

办法3:配置objectMapper

这里的原理跟上面的办法2一样,都是增加Jackson的访问权限范围;

不过这里的配置会在全局,也就是所有的实体类中生效,而不单单是User类;

User类不做改动,在objectMapper添加如下配置:

objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

再次运行,会看到username和nickname都成功序列化;

方法2和方法3,不止可以配置字段的可见性,还可以配置其他地方,比如getter方法,如下所示:

image-20220216112241658

总结

Jackson序列化时如果报错提示:没有找到可以序列化的内容;

那么这种问题一般都是因为待序列化的对象没有可以读取的属性导致的;

比如实体类User没有public权限的field或者getter方法,导致Jackson找不到可以序列化的数据,结果就失败了;

解决办法有三种:

  1. 给实体类添加public权限的getter方法

  2. 类加注解 :@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)

  3. 配置ObjectMapper(全局):objectMapper.setVisibility(PropertyAccessor., JsonAutoDetect.Visibility.ANY);

0

评论区