1. Jackson(Springboot的默认json解析框架)

Jackson官网地址:https://github.com/FasterXML/jackson
Jackson文档地址:https://github.com/FasterXML/jackson-docs

Jackson-databind地址:GitHub - FasterXML/jackson-databind: General data-binding package for Jackson (2.x): works on streaming API (core) implementation(s)
Jackson-databind文档地址:https://github.com/FasterXML/jackson-databind/tree/2.17/docs

Jackson 导入 jackson-databindjackson-annotationsjackson-core 3 个主要模块基本满足开发,其中 jackson-databind内部依赖了 jackson-annotations 与 jackson-core ,所以 Maven 应用时,只要导入 jackson-databind 一个,则同时也导入了 jackson-annotations 与 jackson-core 依赖。

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.11.0</version>
</dependency>

ObjectMapper 是 Jackson 序列化和反序列化的核心类,提供了许多用于定制序列化和反序列化的方法和配置选项。.默认情况下,ObjectMapper 在序列化对象时,将实体所有的字段一 一序列化,无论这些字段是否有值,是否为 null。

注意:如果实体的某个字段没有提供 getter 方法,则该字段不会被序列化。)

ObjectMapper 主要用于对 Java 对象(比如 POJO、List、Set、Map 等等)进行序列化与反序列化。还能将 Json 字符串与 JsonNode 进行转换。

public class TestJacksonObjectMapper {
    @Test
    public void testJacksonObjectMapper() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        User user = new User();
        user.setAge(20);
        user.setBirthday(new Date());
        user.setName("yoooo");
        user.setRegion("中国");
        // 序列化
        String jsonString = objectMapper.writeValueAsString(user);
        System.out.println("序列化字符串:" + jsonString);
        // 反序列化
        User userFromJson = objectMapper.readValue(jsonString, User.class);
        System.out.println("反序列化结果:" + userFromJson);
    }
}

其中,writeValueAsString 方法用于将 Java 对象序列化为 JSON 字符串,readValue 方法用于将 JSON 字符串反序列化为 Java 对象。这里的 User.class 表示需要反序列化成的 Java 对象类型。

1.1 Jackson的核心API

方法说明
String writeValueAsString(Object value)1、用于将任何 Java 对象(如 POJO、List、Set、Map等)序列化为 json 字符串,如果对象中某个属性的值为 null,则默认也会序列化为 null;2、如果 value 为 null,返回序列化的结果也返回 null
byte[] writeValueAsBytes(Object value)将 java 对象序列化为字节数组
writeValue(File resultFile, Object value)将 java 对象序列化并输出指定文件中
writeValue(OutputStream out, Object value)将 java 对象序列化并输出到指定字节输出流中
T readValue(String content, Class valueType)1、从给定的 JSON 字符串反序列化为 Java 对象;2、content 为空或者为 null,都会报错;3、valueType 表示反序列化的结果对象,可以是任何 java 对象,比如 POJO、List、Set、Map 等等.
T readValue(byte[] src, Class valueType)将 json 内容的字节数组反序列化为 java 对象
T readValue(File src, Class valueType)将本地 json 内容的文件反序列化为 java 对象
T readValue(InputStream src, Class valueType)将 json 内容的字节输入流反序列化为 java 对象
T readValue(Reader src, Class valueType)将 json 内容的字符输入流反序列化为 java 对象
T readValue(URL src, Class valueType)通过网络 url 地址将 json 内容反序列化为 java 对象

在调用 writeValue 或调用 readValue 方法之前,往往需要设置 ObjectMapper 的相关配置信息。这些配置信息应用 java 对象的所有属性上。示例如下:

//在反序列化时忽略在 json 中存在但 Java 对象不存在的属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
//在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//在序列化时自定义时间日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
//在序列化时忽略值为 null 的属性
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//在序列化时忽略值为默认值的属性
mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_DEFAULT);

核心 Jackson 注释由 jackson-annotations 核心组件定义,它不依赖于任何其他包

1. @JsonProperty

@JsonProperty:用于在 Java 对象的属性和 JSON 字段之间建立映射关系。通过该注解,可以自定义属性在序列化和反序列化过程中所对应的 JSON 字段的名称。

value:设置属性的名称。一般当JSON串中的key与对象中的属性名称不一致, 比如分别采用了下划线命名与驼峰命名。

defaultValue:用于记录预期的默认值

required:定义在反序列化期间是否需要属性的值

index:如果数据格式(JSON 除外)是基于索引的,则要使用的物理索引

access:更改序列化和反序列化中逻辑属性的可见性

  • A逻辑属性的可见性仅根据可见性和其他注释确定,access的默认值。
  • READ_ONLY 逻辑属性仅在序列化时可见(序列化指从Java对象转成JSON数据)
  • WRITE_ONLY 逻辑属性仅在反序列化时可见(反序列化指JSON数据转为java对象时)
  • READ_WRITE 逻辑属性在序列化和反序列化时都可见

使用实例:在需要映射的属性上添加 @JsonProperty 注解,并指定 JSON 字段的名称作为参数。例如:

public class TestJacksonCoreAnnotations {

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Person p = new Person();
        p.setFirstName("张");
        p.setLastName("三");
        p.setGraduateSchool("清华大学");
        p.setRealAge(20);
        String jsonString = objectMapper.writeValueAsString(p);
        System.out.println(jsonString);

        Person person = objectMapper.readValue(jsonString, Person.class);
        System.out.println(person.toString());
    }

}

@Data
@ToString
class Person {
    @JsonProperty(value = "first_name", index = 0)
    private String firstName;

    @JsonProperty(value = "last_name", access = JsonProperty.Access.READ_ONLY, index = 3)
    private String lastName;

    @JsonProperty(value = "graduate_school", access = JsonProperty.Access.WRITE_ONLY, index = 2)
    private String graduateSchool;

    @JsonProperty(value = "real_age", access = JsonProperty.Access.READ_WRITE, index = 1)
    private Integer realAge;


}

运行结果:

{"first_name":"张","real_age":20,"last_name":"三"}
Person(firstName=张, lastName=null, graduateSchool=null, realAge=20)

在上述示例中,firstName 属性在 JSON 序列化和反序列化时将被映射为 “name” 字段。

  • lastName 由于设置序列化时可见,所以对象中 lastName 的值为 null。
  • graduateSchool 由于设置反序列化时可见,所以序列化JSON串中没有graduateSchool 。
  • realAge 设置都可见,因此在对象和JSON字符串中都有。
  • index 可以理解为在JSON字符串中的出现位置顺序定义。

2. @JsonAlias

@JsonAlias:是在反序列化的时候可以让Bean的属性接收多个json字段的名称

使用场景:当我们在开发过程中,可能在1.0的版本中我们定义的类包含name属性,但是我们用着用着发现name这个单词太通用了,我们希望在2.0版本中,给这个成员变量换一个名字,叫做nickName或者playerName。但是我们不希望用户废弃v1.0的接口。也就是说我们希望做到多版本兼容,客户端传送过来的JSON字符串,可以是namenickNameplayerName。这个时候可以使用@JsonAlias注解。

public class TestJacksonCoreAnnotations {

    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Person p = new Person();
        p.setFirstName("张");
        p.setLastName("三");
        p.setGraduateSchool("清华大学");
        p.setRealAge(20);
        String jsonString = objectMapper.writeValueAsString(p);
        System.out.println(jsonString);
        
        String newString = jsonString.replace("first_name","nick_name");
        System.out.println(newString);

        Person person = objectMapper.readValue(newString, Person.class);
        System.out.println(person.toString());
    }

}

@Data
@ToString
class Person {
    @JsonProperty(value = "first_name", index = 0)
    @JsonAlias(value = {"nick_name","player_name"})
    private String firstName;

    @JsonProperty(value = "last_name", access = JsonProperty.Access.READ_ONLY, index = 3)
    private String lastName;

    @JsonProperty(value = "graduate_school", access = JsonProperty.Access.WRITE_ONLY, index = 2)
    private String graduateSchool;

    @JsonProperty(value = "real_age", access = JsonProperty.Access.READ_WRITE, index = 1)
    private Integer realAge;


}

运行结果:

{"first_name":"张","real_age":20,"last_name":"三"}
{"nick_name":"张","real_age":20,"last_name":"三"}
Person(firstName=张, lastName=null, graduateSchool=null, realAge=20)

3. @JsonAutoDetect

@JsonAutoDetect:用于控制序列化和反序列化过程中Java对象中哪些属性可以被JSON格式化库处理。默认情况下,只有公有的getters才能被自动识别并进行JSON序列化。因此,若Java对象需要在序列化和反序列化过程中使用非公有的成员变量,需添加@JsonAutoDetect注解来告诉JACKSON库何时自动序列化和反序列化成员变量。

4. @JsonIgnore

@JsonIgnore:在json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。一般标记在属性或者方法上,返回的json数据即不包含该属性。


    @JsonIgnore
    private String graduateSchool;

    @JsonIgnore
    private Integer realAge;
Person(firstName=张, lastName=三, graduateSchool=null, realAge=null)

注意:jackson扫描字段的时候是判断如果字段上有如下几个注解,PropertyName则为字段名,如果没有那几个注解PropertyName则为null,取消Ignore的根据就是PropertyName是否有值。

JsonGetter、JsonProperty、JsonSerialize.class、JsonView.class、JsonFormat.class、JsonTypeInfo.class、JsonRawValue.class、JsonUnwrapped.class、JsonBackReference.class、JsonManagedReference.class 这些注解会使Ignore失效

5. @JsonIgnoreProperties

@JsonIgnoreProperties:此注解是注解,作用是json序列化时将java bean中的一些属性忽略掉,序列化和反序列化都受影响。

@Data
@ToString
@JsonIgnoreProperties({"graduate_school", "real_age"})
class Person {
    @JsonProperty(value = "first_name")
    @JsonAlias(value = {"nick_name","player_name"})
    private String firstName;

    @JsonProperty(value = "last_name")
    private String lastName;

    @JsonProperty(value = "graduate_school")
    private String graduateSchool;

    @JsonProperty(value = "real_age")
    private Integer realAge;


}

6. @JsonIgnoreType

@JsonIgnoreType:该类作为别的类的属性时,该属性忽略序列化和反序列化。

public class TestJsonIgnoreType {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper mapper = new ObjectMapper();
        Student student = new Student();
        student.setId(1L);
        student.setName("张三");
        student.setDepartment(new Department("计算机系",12L));
        //序列化
        String json = mapper.writeValueAsString(student);
        System.out.println(json);

        //反序列化
        Student student1 = mapper.readValue(json, Student.class);
        System.out.println(student1.toString());
    }

}


@ToString
class Student {
    private String name;
    private Long id;
    private Department department;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Department getDepartment() {
        return department;
    }

    public void setDepartment(Department department) {
        this.department = department;
    }
}
@AllArgsConstructor
@NoArgsConstructor
@ToString
@JsonIgnoreType
class Department {
    private String name;
    private Long id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

 加了注解之后的运行结果如下,可以发现部门并没有被序列化和反序列化。

{"name":"张三","id":1}
Student(name=张三, id=1, department=null)

7. @JsonInclude

@JsonInclude:是为实体类在接口序列化返回值时增加规则的注解

例如,一个接口需要过滤掉返回值为null的字段,即值为null的字段不返回,可以在实体类中增加如下注解

@JsonInclude(JsonInclude.Include.NON_NULL)

@JsonInclude注解中的规则有:

  • ALWAYS默认值,表示全部序列化,即默认返回全部字段
  • NON_NULL表示值为null就不序列化,即值为null的字段不返回(当实例对象中有OptionalAtomicReferenceAtomicReferenceAtomicReference类型的成员变量时,如果Optional或AtomicReference引用的实例为null,用NON_NULL 不能使该字段不做序列化,此时应使用NON_ABSENT规则)
  • NON_ABSENT可在实例对象中有Optional或AtomicReference类型的成员变量时,如果Optional或AtomicReference引用的实例为null时,也可使该字段不做序列化,同时可以排除值为null的字段
  • NON_EMPTY排除字段值为null、空字符串、空集合、空数组、Optional类型引用为空,AtomicReference类型引用为空
  • NON_DEFAULT没有更改的字段不序列化,即未变动的字段不返回
  • CUSTOM:这个值要配合valueFilter属性一起使用,在序列化的时候会执行CustomFilter中的的equals方法,该方法的入参就是字段的值,如果equals方法返回true,字段就不会被序列化,如果equals方法返回false时字段才会被序列化,即true时不返回,false时才返回

    @JsonInclude(value = JsonInclude.Include.CUSTOM, valueFilter = CustomFilter.class)
    private String field;
    
    static class CustomFilter {
            @Override
            public boolean equals(Object obj) {
                // 为null,或者不是字符串就返回true,即不返回该字段
                if(null == obj || !(obj instanceof String)) {
                    return true;
                }
    
                // 长度大于2就返回true,意味着不被序列化
                return ((String) obj).length() > 2;
            }
        }
    

8. @JsonFormat

@JsonFormat是一个时间格式化注解,比如我们存储在mysql中的数据是date类型的,当我们读取出来封装在实体类中的时候,就会变成英文时间格式,而不是yyyy-MM-dd HH:mm:ss这样的中文时间,因此我们需要用到JsonFormat注解来格式化我们的时间。

  • shape: 表示序列化后的一种类型,默认为JsonFormat.Shape.ANY

  • pattern: 表示日期的格式,比如:yyyy-MM-dd HH:mm:ss

  • timezone: 默认是GMT,中国需要GMT+8

    • 中国时间(Asia/Shanghai) = 格林尼治时间(GMT)+ 8

    • 格林尼治时间(GMT) = 世界协调时间(UTC) + 0

  • locale: 根据位置序列化的一种格式

/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
private Date createTime;

/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone="GMT+8")
private Date updateTime;

9. @JsonUnwrapped

@JsonUnwrapped:表明属性应该以扁平化的形式进行序列化,即目标属性将不会序列化为 JSON 对象,但其属性将序列化为包含它的对象的属性。

public class Department {
    private String deptName;
    private String location;
    ...
}  


public class Employee {
    private String name;
    @JsonUnwrapped
    private Department dept;
    ...
}  

public class ExampleMain {
    public static void main(String[] args) throws IOException {
        Department dept = new Department();
        dept.setDeptName("Admin");
        dept.setLocation("NY");
        Employee employee = new Employee();
        employee.setName("Amy");
        employee.setDept(dept);

        System.out.println("-- before serialization --");
        System.out.println(employee);

        System.out.println("-- after serialization --");
        ObjectMapper om = new ObjectMapper();
        String jsonString = om.writeValueAsString(employee);
        System.out.println(jsonString);

        System.out.println("-- after deserialization --");
        Employee employee2 = om.readValue(jsonString, Employee.class);
        System.out.println(employee2);
    }
}

dept 属性并没有被序列化为 JSON 对象,而是将 dept 的属性作为 Employee 的属性序列化了。

-- before serialization --
Employee{name='Amy', dept=Department{deptName='Admin', location='NY'}}
-- after serialization --
{"name":"Amy","deptName":"Admin","location":"NY"}
-- after deserialization --
Employee{name='Amy', dept=Department{deptName='Admin', location='NY'}}

不使用 @JsonUnwrapped,dept 以 JSON 对象的形式被序列化了

-- before serialization --
Employee{name='Amy', dept=Department{deptName='Admin', location='NY'}}
-- after serialization --
{"name":"Amy","dept":{"deptName":"Admin","location":"NY"}}
-- after deserialization --
Employee{name='Amy', dept=Department{deptName='Admin', location='NY'}}

使用 @JsonUnwrapped 可能出现名称冲突的情况,可以用 @JsonUnwrapped#prefix@JsonUnwrapped#suffix 解决。

public class Department {
    private String name;
    private String location;
    ...
}    

public class Employee {
    private String name;
    @JsonUnwrapped(prefix = "dept-")
    private Department dept;
    ...
}    


-- before serialization --
Employee{name='Amy', dept=Department{name='Admin', location='NY'}}
-- after serialization --
{"name":"Amy","dept-name":"Admin","dept-location":"NY"}
-- after deserialization --
Employee{name='Amy', dept=Department{name='Admin', location='NY'}}



若不使用 @JsonUnwrapped#prefix,Department.name 被反序列化为 null:
-- before serialization --Employee{name='Amy', dept=Department{name='Admin', location='NY'}}-- after serialization --{"name":"Amy","name":"Admin","location":"NY"}-- after deserialization --Employee{name='Admin', dept=Department{name='null', location='NY'}}

10. @JsonView

@JsonView:两个URL,一个为获取用户详情信息包含用户的所有字段(用户名,密码),一个为获取用户信息字段(只要用户名),怎么处理呢?@JsonView可以十分方便的解决以上问题。下面我来介绍一下@JsonView。

    1、使用接口声明多个视图

    2、在对象属性的get方法上指定视图

@Data
@AllArgsConstructor
class UserTwo {
    private String name;
    private String password;

    @JsonView(TestJsonView.UserSimpView.class)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @JsonView(TestJsonView.UserDetailView.class)
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

11. @JacksonInject

@JacksonInject:在使用​ ​JSON​​格式进行反序列化的时候,我们经常有这样一些需求。我们从客户端或者其他渠道获取了一个JSON格式的数据对象,该对象包含若干个属性。但是我们在将JSON字符串反序列化的时候,需要给它加上一些默认数据,比如:

  • responseTime数据响应时间,赋值为当前时间即可;
  • 数据反序列化的操作人,赋值为系统当前用户等

客户端返回给我们的数据本身不会携带这些附加信息,这个时候我们就可以使用@JacksonInject注解,在JSON字符串反序列化为对象的时候,加上这些附加信息。

@Data
public class PlayerStar {
    private String name;
    private Integer age;
    // 业余爱好,数组
    private String[] hobbies;
    // 朋友
    private List<String> friends;
    // 年收入 Map
    private Map<String, BigDecimal> salary;

    @JacksonInject("responseTime")
    private LocalDateTime responseTime;
}
public class App {

    public static void main(String[] args) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();

        // 为responseTime赋值为当前值
        InjectableValues.Std injectableValues = new InjectableValues.Std();
        injectableValues.addValue("responseTime", LocalDateTime.now());

        // 在反序列化过程中赋值给对象
        objectMapper.setInjectableValues(injectableValues);

        String jsonInString = "{\"name\":\"乔丹\",\"age\":45,\"hobbies\":[\"高尔夫球\",\"棒球\"]}";
        PlayerStar jordan = objectMapper.readValue(jsonInString, PlayerStar.class);

        System.out.println(jordan);
    }

}

最终的反序列化结果,java 对象的toString()方法输出结果如下,可以看到 多出一个responseTime赋值属性,值为当前时间

PlayerStar(name=乔丹, age=45, hobbies=[高尔夫球, 棒球], friends=null, salary=null, responseTime=2020-12-03T13:41:52.932)

12. @JsonAnyGetter和@JsonAnySetter

@JsonAnySetter:可以将序列化时未识别到的属性都扔进一个map里。

@JsonAnyGetter:将map里的属性都平铺输出。

public class TestAnyGetterAndSetter {
    public static void main(String[] args) throws JsonProcessingException {
        String json1 = "{\"name\":\"zhangSan\",\"id\":1,\"address\":\"china\"}";
        ObjectMapper objectMapper = new ObjectMapper();
        Account account = objectMapper.readValue(json1, Account.class);
        System.out.println(account.getMap());
        String jsonString = objectMapper.writeValueAsString(account);
        System.out.println(jsonString);
    }


}
class Account {

    private long id;
    private String name;
    private Map<String, Object> map;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @JsonAnySetter
    public void setMap(String key, Object value) {
        if (map == null) {
            map = new HashMap<>();
        }
        map.put(key, value);
    }

    @JsonAnyGetter
    public Map<String, Object> getMap() {
        return map;
    }
}

运行结果:可以看到,反序列化时未识别到的属性address被塞进了map属性了,另外在反序列时,也可以输出到Account类其他属性的平级位置。

{address=china}
{"id":1,"name":"zhangSan","address":"china"}

13. @JsonSetter 和 @JsonGetter

@JsonSetter 和 @JsonGetter@JsonSetter只能用于setter方法,@JsonGetter只能用户getter方法,只用二者中的一个标签时,与@JsonProperty作用一模一样。

这样一种情景:对于一个java的pojo类的一个属性,我们希望序列化时是一个属性名反序列化时是另一个属性名。

14. @JsonCreator

@JsonCreator(静态的工厂方法、构造方法上):加了@JsonCreator注解,该类的对象在反序列化时,就走有@JsonCreator注解的方法来反序列化,原先无参+set的过程失效。

注意:反序列化时,默认会调用实体类的无参构造来实例化一个对象,然后使用setter来初始化属性值。如果我们想走别的方法来实例化对象,就需要@JsonCreator方法

public class TestJsonCreator {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Page page = objectMapper.readValue("{" +
                "    \"page_size\":12,\n" +
                "    \"page_num\":1,\n" +
                "    \"name\":\"Java\"\n" +
                "}",Page.class);
        System.out.println(page);
    }
}
@Data
class Page {
    @JsonProperty(value = "page_size")
    private int pageSize;

    @JsonProperty(value = "page_num")
    private int pageNum;

    private String name;

    @JsonCreator
    public Page(@JsonProperty("name")String name,
                @JsonProperty("page_num")int pageNum,
                @JsonProperty("page_size")int pageSize){
        System.out.println("@JsonCreator生效");
        this.pageNum = pageNum;
        this.pageSize = pageSize;
        this.name = name;
    }
}

加在静态方法上:

public class TestJsonCreator {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        Page page = objectMapper.readValue("{" +
                "    \"page_size\":12,\n" +
                "    \"page_num\":1,\n" +
                "    \"name\":\"Java\"\n" +
                "}",Page.class);
        System.out.println(page);
    }
}
@Data
@AllArgsConstructor
class Page {
    @JsonProperty(value = "page_size")
    private int pageSize;

    @JsonProperty(value = "page_num")
    private int pageNum;

    private String name;


    @JsonCreator
    public static Page unSerialize(){
        System.out.println("正在反序列化成对象");
        return new Page(12,1, "Java");
    }
}

运行结果: 

正在反序列化成对象
Page(pageSize=12, pageNum=1, name=Java)

15. @JsonValue

@JsonValue(get方法、属性字段):一个类只能用一个,加上这个注解时,该类的对象序列化时就会只返回这个字段的值做为序列化的结果。比如一个枚举类的get方法上加上该注解,那么在序列化这个枚举类的对象时,返回的就是枚举对象的这个属性,而不是这个枚举对象的序列化json串。

 /**
     * 序列化时,序列化成我return的值,即当前对象的name属性
     */
    @JsonValue
    public String getName(){
        return this.name;
    }

这里引用其他博主的一篇关于此注解的应用,方便查阅。

@JsonCreator和@JsonValue-CSDN博客

16. @JsonPropertyOrder

@JsonPropertyOrder:用于指定实体生成 json 时的属性顺序,一般用得不多

// 自定义顺序
@JsonPropertyOrder({"name", "attributes", "indexes"})
//按字母排序
@JsonPropertyOrder(alphabetic=true)

17. @JsonRawValue

@JsonRawValue:能够按原样序列化属性。属性值不会被转义或者加引号(或者说,会自动去掉转义,多余的引号)。属性值已经是一个 JSON String,或者属性值已经被加了引号时很有用。

public class TestJsonRawValue {
    public static void main(String[] args) throws JsonProcessingException {
        News news = new News();
        news.setContent("\"中国No.1\"");
        news.setTitle("中国崛起!");
        news.setPublishTime(new Date());
        System.out.println("-- before serialization --");
        ObjectMapper objectMapper = new ObjectMapper();

        String jsonString = objectMapper.writeValueAsString(news);
        System.out.println(jsonString);

        News news1 = objectMapper.readValue(jsonString, News.class);
        System.out.println(news1);
    }
}
@Data
class News {
    @JsonRawValue
    private String content;
    private String title;
    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date publishTime;
}

如果不加@JsonRawValue注解:

-- before serialization --
{"content":"\"中国No.1\"","title":"中国崛起!","publishTime":"2024-02-28 10:47:00"}
News(content="中国No.1", title=中国崛起!, publishTime=Wed Feb 28 10:47:00 CST 2024)

加了注解之后:

-- before serialization --
{"content":"中国No.1","title":"中国崛起!","publishTime":"2024-02-28 10:47:41"}
News(content=中国No.1, title=中国崛起!, publishTime=Wed Feb 28 10:47:41 CST 2024)

18. @JsonRootName

@JsonRootName:用来指定root wrapper的名字。注意,只有当WRAP_ROOT_VALUE开启时,此注解才生效。

比如:一般序列化 User是这样的格式 { "id": 1, "name": "yooo" },但是我想添加一个root wrapper挂载在"User"节点下,例如

{
    "User": {
        "id": 1,
        "name": "yooo"
    }
}
public class TestJsonRootName {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        UserDetail userDetail = new UserDetail();
        userDetail.setName("yooo");
        userDetail.setId(12);
        String jsonString = objectMapper.writeValueAsString(userDetail);
        System.out.println(jsonString);
        UserDetail userDetail1 = objectMapper.readValue(jsonString, UserDetail.class);
        System.out.println(userDetail1);
    }
}
@JsonRootName("User")
@Data
class UserDetail{
    private String name;
    private Integer id;
}

如果`WRAP_ROOT_VALUE`不开启,此注解不生效。

{"name":"yooo","id":12}
UserDetail(name=yooo, id=12)

只有添加了如下两行代码,序列化和反序列化才能生效

objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
{"User":{"name":"yooo","id":12}}
UserDetail(name=yooo, id=12)

19.@JacksonAnnotationsInside

我们需要自定义注解来标注类属性,来达到批量解析类属性的目的,而且并且我们希望被这个注解标志的属性不被 Json 序列化(不希望返回给前端)。通常情况下会采用@JsonIgnore

public class User{
	
	@CustomAnnotation // 自定义的注解
	@JsonIgnore
	private String name;
	
}

但是如果使用该自定义注解的地方有多处,那么每一处都需要这么处理,很麻烦。那么有没有办法能够让我们自定义的注解同时拥有@JsonIgnore的特性呢?

因此,Jackson 给我们提供了一个@JacksonAnnotationsInside注解,我们只要在自定义注解上添加@JacksonAnnotationsInside注解,这样Jackson会把被@JacksonAnnotation标注的注解作为一个组合注解,并扫描该注解上的注解,这样我们在该注解上的Jackson相关的注解就会起作用。例如,下面就是将@JacksonAnnotationsInside注解进行组合注解的一个例子。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
@JsonSerialize(using = SensitiveSerializeHandler.class)
public @interface Sensitive {

}

jackson-databind 添加了一小组与 databind 包中定义的类型相关的注释。这些用于更细粒度的处理程序定义。 

20. @JsonSerialize

@JsonSerialize(用于字段或get方法上):主要用于定制 Java 对象到 JSON 的序列化过程。通过在 Java 类或属性上使用 @JsonSerialize注解,开发者可以指定一个自定义的序列化器,从而掌握对象如何被转换为 JSON 。这种灵活性使得开发者能够更好地应对各种复杂的序列化需求。

使用场景:处理敏感信息、处理日期格式、处理特殊类型数据或者逻辑

public class TestJsonSerializeAndJsonDeSerialize {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        String string = objectMapper.writeValueAsString(new Example(new Date()));
        System.out.println(string);
        System.out.println(objectMapper.readValue(string, Example.class));
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Example{
    @JsonSerialize(using = DateSerializer.class)
    private Date time;
}
class DateSerializer extends JsonSerializer<Date> {

    private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
    @Override
    public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        String formattedDate = simpleDateFormat.format(date);
        jsonGenerator.writeString(formattedDate);
    }
}

21.@JsonDeserialize

@JsonDeserialize(用于字段或set方法上):json反序列化注解,用于字段或set方法上,作用于setter()方法,将json数据反序列化为java对象。使用方法同@JsonSerialize类似。

这里引用博主的一篇文章,供大家一起学习:@JsonSerialize和@JsonDeserialize的使用详解-CSDN博客

1.2 Jackson 集成 SpringBoot 

1. 导入 Jackson 的依赖

在使用 Jackson 之前,可以通过Maven项目中导入 Jackson 的依赖,通过在 pom.xml 文件中添加jackson-databind即可

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.0</version>
</dependency>

2. 自定义 Jackson 的配置

配置大全

spring:
  jackson:
    # 设置属性命名策略,对应jackson下PropertyNamingStrategy中的常量值,SNAKE_CASE-返回的json驼峰式转下划线,json body下划线传到后端自动转驼峰式
    property-naming-strategy: SNAKE_CASE
    # 全局设置@JsonFormat的格式pattern
    date-format: yyyy-MM-dd HH:mm:ss
    # 当地时区
    locale: zh_CN
    # 设置全局时区
    time-zone: GMT+8
    # 常用,全局设置pojo或被@JsonInclude注解的属性的序列化方式
    default-property-inclusion: NON_NULL #不为空的属性才会序列化,具体属性可看JsonInclude.Include
    # 常规默认,枚举类SerializationFeature中的枚举属性为key,值为boolean设置jackson序列化特性,具体key请看SerializationFeature源码
    visibility:
      #属性序列化的可见范围
      getter: non_private
      #属性反序列化的可见范围
      setter: protected_and_public
      #静态工厂方法的反序列化
      CREATOR: public_only
      #字段
      FIELD: public_only
      #布尔的序列化
      IS_GETTER: public_only
      #所有类型(即getter setter FIELD)不受影响,无意义
      NONE: public_only
      #所有类型(即getter setter FIELD)都受其影响(慎用)
      ALL: public_only
    serialization:
      #反序列化是否有根节点
      WRAP_ROOT_VALUE: false
      #是否使用缩进,格式化输出
      INDENT_OUTPUT: false
      FAIL_ON_EMPTY_BEANS: true # 对象不含任何字段时是否报错,默认true
      FAIL_ON_SELF_REFERENCES: true #循环引用报错
      WRAP_EXCEPTIONS: true #是否包装异常
      FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS: true #JsonUnwrapped标记的类有类型信息是否报错
      WRITE_SELF_REFERENCES_AS_NULL: false #循环引用返回null
      CLOSE_CLOSEABLE: true #若对象实现了CLOSEABLE接口,在序列化后是否调用Close方法
      FLUSH_AFTER_WRITE_VALUE: false #流对象序列化之后是否强制刷新
      WRITE_DATES_AS_TIMESTAMPS: true # 返回的java.util.date转换成时间戳
      WRITE_DATES_WITH_ZONE_ID: true #2011-12-03T10:15:30+01:00[Europe/Paris]带时区id
      WRITE_DURATIONS_AS_TIMESTAMPS: true #将DURATIONS转换成时间戳
      WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS: false #是否字符数组输出json数组 (false则输出字符串)
      WRITE_ENUMS_USING_TO_STRING: false # 将枚举输出toString
      WRITE_ENUMS_USING_INDEX: false #枚举下标
      WRITE_ENUM_KEYS_USING_INDEX: false #枚举key类似
      WRITE_NULL_MAP_VALUES: false #是否输出map中的空entry(此特性已过期,请使用JsonInclude注解)
      WRITE_EMPTY_JSON_ARRAYS: true # 对象属性值是空集合是否输出空json数组
      WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED: false #是否将单个元素的集合展开,(即:去除数组符号"[]")
      WRITE_BIGDECIMAL_AS_PLAIN: false #是否调用BigDecimal#toPlainString()输出
      WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS: #将timestamp输出为纳秒
      ORDER_MAP_ENTRIES_BY_KEYS: false #map序列化后,是否用key对其排序
      EAGER_SERIALIZER_FETCH: true #是否马上获取序列化器
      USE_EQUALITY_FOR_OBJECT_ID: false #是否使用objectId比较是否相等(在ORM框架Hibernate中有应用)
 
    # 枚举类DeserializationFeature中的枚举属性为key,值为boolean设置jackson反序列化特性,具体key请看DeserializationFeature源码
    deserialization:
      USE_BIG_DECIMAL_FOR_FLOATS: false #将浮点数反序列化为BIG_DECIMAL
      USE_BIG_INTEGER_FOR_INTS: false #将整数反序列化为BIG_INTEGER
      USE_LONG_FOR_INTS: false #将整型反序列化为长整
      USE_JAVA_ARRAY_FOR_JSON_ARRAY: false #无明确类型时,是否将json数组反序列化为java数组(若是true,就对应Object[] ,反之就是List<?>)
      FAIL_ON_UNKNOWN_PROPERTIES: false # 常用,json中含pojo不存在属性时是否失败报错,默认true
      FAIL_ON_NULL_FOR_PRIMITIVES: false #将null反序列化为基本数据类型是否报错
      FAIL_ON_NUMBERS_FOR_ENUMS: false #用整数反序列化为枚举是否报错
      FAIL_ON_INVALID_SUBTYPE: false #找不至合适的子类否报错 (如注解JsonTypeInfo指定的子类型)
      FAIL_ON_READING_DUP_TREE_KEY: false #出现重复的json字段是否报错
      FAIL_ON_IGNORED_PROPERTIES: false #如果json中出现了java实体字段中已显式标记应当忽略的字段,是否报错
      FAIL_ON_UNRESOLVED_OBJECT_IDS: true #如果反序列化发生了不可解析的ObjectId是否报错
      FAIL_ON_MISSING_CREATOR_PROPERTIES: false #如果缺少静态工厂方法的参数是否报错(false,则使用null代替需要的参数)
      FAIL_ON_NULL_CREATOR_PROPERTIES: false #将空值绑定到构造方法或静态工厂方法的参数是否报错
      FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY: false #注解JsonTypeInfo.As#EXTERNAL_PROPERTY标记的属性缺失,是否报错
      FAIL_ON_TRAILING_TOKENS: false #出现尾随令牌是否报错(如果是true,则调用JsonParser#nextToken,检查json的完整性)
      WRAP_EXCEPTIONS: true #是否包装反序列化出现的异常
      ACCEPT_SINGLE_VALUE_AS_ARRAY: true #反序列化时是否将一个对象封装成单元素数组
      UNWRAP_SINGLE_VALUE_ARRAYS: false #反序列化时是否将单元素数组展开为一个对象
      UNWRAP_ROOT_VALUE: false #是否将取消根节点的包装
      ACCEPT_EMPTY_STRING_AS_NULL_OBJECT: false #是否将空字符("")串当作null对象
      ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT: false #是否接受将空数组("[]")作为null
      ACCEPT_FLOAT_AS_INT: true #是否接受将浮点数作为整数
      READ_ENUMS_USING_TO_STRING: false #按照枚举toString()方法读取,(false则按枚举的name()方法读取)
      READ_UNKNOWN_ENUM_VALUES_AS_NULL: false #读取到未知的枚举当作null
      READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE: false #读取到未知的枚举,将其当作被JsonEnumDefaultValue注解标记的枚举
      READ_DATE_TIMESTAMPS_AS_NANOSECONDS: true #将时间戳视为纳秒(false,则视为毫秒)
      ADJUST_DATES_TO_CONTEXT_TIME_ZONE: true #反序列化是否会适应DeserializationContext#getTimeZone()提供的时区 (此特性仅对java8的时间/日期有效)
      EAGER_DESERIALIZER_FETCH: true  #是否马上获取反序列化器
    # 枚举类MapperFeature中的枚举属性为key,值为boolean设置jackson ObjectMapper特性
    # ObjectMapper在jackson中负责json的读写、json与pojo的互转、json tree的互转,具体特性请看MapperFeature,常规默认即可
    mapper:
      USE_ANNOTATIONS: true #是否使用注解自省(检查JsonProperties这些)
      # 使用getter取代setter探测属性,这是针对集合类型,可以直接修改集合的属性
      USE_GETTERS_AS_SETTERS: true #默认false
      PROPAGATE_TRANSIENT_MARKER: false #如何处理transient字段,如果true(不能访问此属性) ,若是false则不能通过字段访问(还是可以使用getter和setter访问)
      AUTO_DETECT_CREATORS: true #是否自动检测构造方法或单参且名为valueOf的静态工厂方法
      AUTO_DETECT_FIELDS: true #是否自动检测字段 (若true,则将所有public实例字段视为为属性)
      AUTO_DETECT_GETTERS: true #确定是否根据标准 Bean 命名约定自动检测常规“getter”方法的(不包括is getter)
      AUTO_DETECT_IS_GETTERS: true #确定是否根据标准 Bean 命名约定自动检测“is getter”方法
      AUTO_DETECT_SETTERS: false # 确定是否根据标准 Bean 命名约定自动检测“setter”方法
      REQUIRE_SETTERS_FOR_GETTERS: false #getter方法必需要有对应的setter或字段或构造方法参数,才能视为一个属性
      ALLOW_FINAL_FIELDS_AS_MUTATORS: true #是否可以修改final成员字段
      INFER_PROPERTY_MUTATORS: true #是否能推断属性,(即使用字段和setter是不可见的,但getter可见即可推断属性)
      INFER_CREATOR_FROM_CONSTRUCTOR_PROPERTIES: true #是否自动推断ConstructorProperties注解
      CAN_OVERRIDE_ACCESS_MODIFIERS: true #调用AccessibleObject#setAccessible设为true .将原来不可见的属性,变为可见
      OVERRIDE_PUBLIC_ACCESS_MODIFIERS: true #对所有的属性调用AccessibleObject#setAccessible设为true .(即使用是公共的)
      USE_STATIC_TYPING: false #序列化使用声明的静态类型还是动态类型  JsonSerialize#typing注解可覆盖它
      USE_BASE_TYPE_AS_DEFAULT_IMPL: false # 反序列化是否使用基本类作为默实现 @JsonTypeInfo.defaultImpl
      DEFAULT_VIEW_INCLUSION: true #没有JsonView注解标记的属性是否会被包含在json序列化视图中
      SORT_PROPERTIES_ALPHABETICALLY: false #按字母表顺序序列化字段(若false,按字段声明的顺序)
      ACCEPT_CASE_INSENSITIVE_PROPERTIES: false #反序列化属性时不区分大小写 (true时,会影响性能)
      ACCEPT_CASE_INSENSITIVE_ENUMS: false #枚举反序列化不区别大小写
      ACCEPT_CASE_INSENSITIVE_VALUES: false #允许解析一些枚举的基于文本的值类型但忽略反序列化值的大小写 如日期/时间类型反序列化器
      USE_WRAPPER_NAME_AS_PROPERTY_NAME: false # 使用包装器名称覆盖属性名称 AnnotationIntrospector#findWrapperName指定的
      USE_STD_BEAN_NAMING: false # 是否以强制与 Bean 名称自省严格兼容的功能,若开启后(getURL())变成URL (jackson默认false, url)
      ALLOW_EXPLICIT_PROPERTY_RENAMING: false #是否允许JsonProperty注解覆盖PropertyNamingStrategy
      ALLOW_COERCION_OF_SCALARS: true # 是否允许强制使用文本标题 ,即将字符串的"true"当作布尔的true ,字符串的"1.0"当作"double"
      IGNORE_DUPLICATE_MODULE_REGISTRATIONS: true #如果模块相同(Module#getTypeId()返回值相同),只有第一次能会真正调用注册方法
      IGNORE_MERGE_FOR_UNMERGEABLE: true #在合并不能合并的属性时是否忽略错误
      BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES: false #阻止不安全的基类(如Object Closeable Cloneable AutoCloseable Serializable)
    parser:
      AUTO_CLOSE_SOURCE: true #是否自动关闭不属于解析器的底层输入流
      ALLOW_COMMENTS: false #是否允许json注解(Json规范是不能加注释的,但这里可以配置)
      ALLOW_YAML_COMMENTS: false #是否允许出现yaml注释
      ALLOW_UNQUOTED_FIELD_NAMES: false #是否允许出现字段名不带引号
      ALLOW_SINGLE_QUOTES: false # 是否允许出现单引号,默认false
      ALLOW_UNQUOTED_CONTROL_CHARS: false #是否允许出现未加转义的控制字符
      ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER: false #是否允许对所有字符都可加反斜杠转义
      ALLOW_NUMERIC_LEADING_ZEROS: false #是否允许前导的零 000001
      ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS: false #是否允许前导的小点数 如 ".04314"会被解析成"0.04314"
      ALLOW_NON_NUMERIC_NUMBERS: false #是否允许NaN型的浮点数 ("INF"当作正无穷  "-INF"当作负无穷 "NaN"非数字,类型于除数为0)
      ALLOW_MISSING_VALUES: false # 是否允许json数组中出现缺失值 (如["value1",,"value3",]将被反序列化为["value1", null, "value3", null])
      ALLOW_TRAILING_COMMA: false # 是否允许json尾部有逗号 (如{"a": true,})
      STRICT_DUPLICATE_DETECTION: false #是否启用严格的字段名重复检查(开启后会增加20-30%左右的性能开销)
      IGNORE_UNDEFINED: false #属性定义未找到是否报错(这不是针对json,是针对Avro, protobuf等需要Schema的格式)
      INCLUDE_SOURCE_IN_LOCATION: false #是否包含其源信息(如总字节数,总字符数 行号 列号 )
    generator:
      AUTO_CLOSE_TARGET: true #是否自动关闭不属于生成器的底层输出流
      AUTO_CLOSE_JSON_CONTENT: true #是否自动补全json(当有不匹配的JsonToken#START_ARRAY JsonToken#START_OBJECT时)
      FLUSH_PASSED_TO_STREAM: true #是否刷新generator
      QUOTE_FIELD_NAMES: true #是否为字段名添加引号
      QUOTE_NON_NUMERIC_NUMBERS: true #对于NaN浮点数是否加引号
      ESCAPE_NON_ASCII: false #非ASCII码是否需要转义
      WRITE_NUMBERS_AS_STRINGS: false #将数字当作字符串输出 (防止Javascript长度限制被截断)
      WRITE_BIGDECIMAL_AS_PLAIN: false #按BigDecimal的toPlainString()输出
      STRICT_DUPLICATE_DETECTION: false #是否启用严格的字段名重复检查
      IGNORE_UNKNOWN: false #属性定义未找到是否报错(这不是针对json,是针对Avro, protobuf等需要Schema的格式)
 

这里介绍两种方式,第一种采用配置类的方式进行自定义Jackson的配置,例如

@Configuration
public class MapperConfig {

    @Bean
    public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder(){
        return new Jackson2ObjectMapperBuilder()
                // 配置日期格式
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
                // 配置时区
                .timeZone(TimeZone.getTimeZone("Asia/Shanghai"))
                // 配置序列化时包含哪些属性
                .serializationInclusion(JsonInclude.Include.NON_NULL)
                // 配置禁用一些特性
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                // 配置启用一些特性
                .featuresToEnable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);

    }
}

第二种,采用配置文件的方式进行自定义Jackson的配置,例如

spring:
  jackson:
    #日期格式化
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
    #设置空如何序列化
    default-property-inclusion: non_null    
    serialization:
       #格式化输出 
      indent_output: true
      #忽略无法转换的对象
      fail_on_empty_beans: false
    deserialization:
      #允许对象忽略json中不存在的属性
      fail_on_unknown_properties: false
    parser:
      #允许出现特殊字符和转义符
      allow_unquoted_control_chars: true
      #允许出现单引号
      allow_single_quotes: true

在完成上述步骤后,我们就可以在 Spring Boot 中使用 Jackson 了。以下是一个简单的示例:

@RestController
public class TestController {

    @GetMapping("/mvcProcess")
    public List<User> testMVC(){
        List<User> users = new ArrayList<>();
        users.add(new User("Alice", 20,new Date()));
        users.add(new User("Bob", 30,new Date()));
        return users;
    }
}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐