Jackson ObjectMapper使用和常用注解

  • 一、前言
  • 1. 引入Jackson
  • 二、ObjectMapper
  • 1. 创建ObjectMapper
  • 2. 序列化
  • 2.1 Java对象 转 JSON
  • 2.2 Java List 转 JSON
  • 2.3 Java Map 转 JSON
  • 2.4 美化输出格式
  • 2.5 序列化结果写文件
  • 3. 反序列化
  • 3.1 JSON 转 Java对象
  • 3.2 JSON 转 Java List
  • 3.3 JSON 转 Java Map
  • 3.4 JSON File 转 Java对象
  • 3.5 JSON Reader 转 Java对象
  • 3.6 JSON InputStream 转 Java对象
  • 3.7 JSON Byte Array 转 Java对象
  • 3.8 JSON via URL 转 Java对象
  • 三、Jackson注解
  • 1. 序列化/反序列化都生效注解
  • 1.1 @JsonIgnore
  • 1.2 @JsonIgnoreProperties
  • 1.3 @JsonIgnoreType
  • 1.4 @JsonProperty
  • 1.5 @JsonAnyGetter和@JsonAnySetter
  • 1.5.1 非嵌套的otherAttributes
  • 1.5.2 嵌套的otherAttributes
  • 2. 仅序列化时生效注解
  • 2.1 @JsonFormat
  • 2.2 @JsonInclude
  • 2.3 @JsonPropertyOrder
  • 2.4 @JsonView
  • 2.5 @JsonRawValue
  • 四、结语



SpringBoot教程(11) Jackson中的JsonNode,ObjectNode,ArrayNode使用和区别


SpringBoot教程(12) Jackson中的JsonGenerator案例

一、前言

Jackson和Fastjson都是比较出名的JSON解析库,SpringMVC默认使用的是Jackson,而且在企业项目中也大多使用的是Jackson,感觉还是很有必要熟悉Jackson的基本使用的。

1. 引入Jackson

如果只是简单的Java代码,不使用Spring Boot的话,直接引入Jackson。

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

如果是使用了SpringBoot的项目,引入spring-boot-starter-web就会自动引入Jackson。

<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

二、ObjectMapper

我们使用Jackson等工具时,最常见的场景就是JSON的序列化和反序列化。而Jackson最常用的的就是ObjectMapper, 它提供了丰富的方法。

1. 创建ObjectMapper

如果是普通Java项目,则new一个ObjectMapper。

private ObjectMapper mapper = new ObjectMapper();

如果是使用Spring项目,则自动注入ObjectMapper。

@Autowired
private ObjectMapper objectMapper;

2. 序列化

假设有一个Java类:Student。

2.1 Java对象 转 JSON

Student student = getStudent();
String studentStr = mapper.writeValueAsString(student);

2.2 Java List 转 JSON

List<Student> studentList= getStudentList();
String studentListStr = mapper.writeValueAsString(studentList);

2.3 Java Map 转 JSON

Map<String, Object> studentMap = new HashMap<>();
studentMap.put("id", "1");
studentMap.put("name", "亚瑟");
studentMap.put("age", 33);

String studentJsonStr = mapper.writeValueAsString(studentMap);

2.4 美化输出格式

在调writeValueAsString之前先调writerWithDefaultPrettyPrinter

String studentStr = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(student);

2.5 序列化结果写文件

mapper.writeValue(new File(OBJECT_FILE_PATH_FOR_WRITE_FROM_SRC), student);

3. 反序列化

3.1 JSON 转 Java对象

String studentStr = getStudentString();
Student student = mapper.readValue(studentStr, Student.class);

3.2 JSON 转 Java List

List<Student> studentList1 = mapper.readValue(studentListStr , new TypeReference<>() {});
或者
List<Student> studentList2 = Arrays.asList(mapper.readValue(studentListStr, Student[].class));

3.3 JSON 转 Java Map

HashMap studentMap = mapper.readValue(studentStr, HashMap.class);

3.4 JSON File 转 Java对象

File file = new File(OBJECT_FILE_PATH_FROM_SRC);
Student student = mapper.readValue(file, Student.class);

3.5 JSON Reader 转 Java对象

File file = new File(OBJECT_FILE_PATH_FROM_SRC);
Reader reader = new java.io.FileReader(file);
Student student = mapper.readValue(reader, Student.class);

3.6 JSON InputStream 转 Java对象

InputStream inputStream = new FileInputStream(OBJECT_FILE_PATH_FROM_SRC);
Student student = mapper.readValue(inputStream, Student.class);

3.7 JSON Byte Array 转 Java对象

Student student = mapper.readValue(studentStr.getBytes(StandardCharsets.UTF_8), Student.class);

3.8 JSON via URL 转 Java对象

URL url = new URL("file:" + OBJECT_FILE_PATH_FROM_SRC);
Student student6 = mapper.readValue(url, Student.class);

三、Jackson注解

1. 序列化/反序列化都生效注解

1.1 @JsonIgnore

工作中一般会修饰Java类的属性上,无论序列化还是反序列化,Jackson都会忽略这个属性。
举个例子:@JsonIgnore修饰id属性

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentTestForJsonIgnore {

    @JsonIgnore
    private String id;

    private String name;
    
    //注意得用Integer,而不能用int
    private Integer age;
}

序列化:

StudentTestForJsonIgnore stu = new StudentTestForJsonIgnore("1", "亚瑟", 30);
String stuStr = mapper.writeValueAsString(stu);
System.out.println(stuStr);

打印序列化结果: 忽略了id

{"name":"亚瑟","age":30}

反序列化:

String stuFullStr = "{\"id\":\"1\",\"name\":\"亚瑟\",\"age\":30}";
StudentTestForJsonIgnore stu2 = mapper.readValue(stuFullStr, StudentTestForJsonIgnore.class);
System.out.println(stu2.toString());

打印反序列化结果: 忽略了id

StudentTestForJsonIgnore(id=null, name=亚瑟, age=30)

1.2 @JsonIgnoreProperties

@JsonIgnoreProperties的作用和@JsonIgnore类似,但是@JsonIgnoreProperties修饰在Java类上,它可设置忽略多个属性,且可以设置ignoreUnknown = true,反序列化时,忽略在JSON中存在,但在Java类中不存在的字段,而不报异常。
举个例子:@JsonIgnoreProperties设置了"id", “age”,且设ignoreUnknown = true

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonIgnoreProperties(value = {"id", "age"}, ignoreUnknown = true)
public class StudentTestForJsonIgnoreProperties {

    private String id;

    private String name;

    private Integer age;
}

序列化:

StudentTestForJsonIgnoreProperties stu = new StudentTestForJsonIgnoreProperties("1", "亚瑟", 30);
String stuStr = mapper.writeValueAsString(stu);
System.out.println(stuStr);

打印序列化结果: 忽略了"id", "age"这2个属性

{"name":"亚瑟"}

反序列化:

String stuFullStr = "{\"id\":\"1\",\"name\":\"亚瑟\",\"age\":30, \"nickName\":\"Yase\"}";    
StudentTestForJsonIgnoreProperties stu2 = mapper.readValue(stuFullStr, StudentTestForJsonIgnoreProperties.class);
System.out.println(stu2.toString());

打印反序列化结果:

StudentTestForJsonIgnoreProperties(id=null, name=亚瑟, age=null)

1.3 @JsonIgnoreType

当其他类有该类作为属性时,该属性将被忽略。
举例:关于Student的类,添加了englishName属性。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentTestForJsonIgnoreType {

    private String id;

    private String name;

    private EnglishName englishName;

    private Integer age;
}

EnglishName 类定义如下,用@JsonIgnoreType修饰

@Data
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreType
public class EnglishName {
    public String firstName;
    public String lastName;
}

序列化:

StudentTestForJsonIgnoreType stu = new StudentTestForJsonIgnoreType("1", "亚瑟", new EnglishName("Ya", "SE"), 30);
String stuStr = mapper.writeValueAsString(stu);
System.out.println(stuStr);

打印序列化结果: 忽略了englishName属性

{"id":"1","name":"亚瑟","age":30}

反序列化:

String stuFullStr = "{\"id\":\"1\",\"name\":\"亚瑟\",\"age\":30, \"englishName\":{\"firstName\":\"Ya\",\"lastName\":\"SE\"}}";
StudentTestForJsonIgnoreType stu2 = mapper.readValue(stuFullStr, StudentTestForJsonIgnoreType.class);
System.out.println(stu2.toString());

打印反序列化结果:

StudentTestForJsonIgnoreType(id=1, name=亚瑟, englishName=null, age=30)

1.4 @JsonProperty

如果JSON中字段名和Java类中的属性名不一致时,可以用@JsonProperty修饰在属性上。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentTestForJsonProperty {

    private String id;

    @JsonProperty("studentName")
    private String name;

    private Integer age;
}

序列化:

StudentTestForJsonProperty stu = new StudentTestForJsonProperty("1", "亚瑟", 30);
String stuStr = mapper.writeValueAsString(stu);
System.out.println(stuStr);

打印序列化结果: studentName代替了name

{"id":"1","age":30,"studentName":"亚瑟"}

反序列化:

String stuFullStr = "{\"id\":\"1\",\"studentName\":\"亚瑟\",\"age\":30}";
StudentTestForJsonProperty stu2 = mapper.readValue(stuFullStr, StudentTestForJsonProperty.class);
System.out.println(stu2.toString());

打印反序列化结果: studentName的值赋值给了name属性

StudentTestForJsonProperty(id=1, name=亚瑟, age=30)

1.5 @JsonAnyGetter和@JsonAnySetter

@JsonAnyGetter
1.方法是非静态,没有参数的,方法名随意
2.方法返回值必须是Map类型
3.在一个实体类中仅仅用在一个方法上
4.序列化的时候json字段的key就是返回Map的key,value就是Map的value

@JsonAnySetter
1.用在非静态方法上,注解的方法必须有两个参数,第一个是json字段中的key,第二个是value,方法名随意
2.也可以用在Map对象属性上面,建议用在Map对象属性上面
3.反序列化的时候将对应不上的字段全部放到Map里面

1.5.1 非嵌套的otherAttributes
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentTestForJsonGetSet {

    private String id;

    private String name;

    private Integer age;

    private Map<String, Object> otherAttributes = new HashMap<>();

    @JsonAnyGetter
    public Map<String, Object> getOtherAttributes() {
        return this.otherAttributes;
    }

    @JsonAnySetter
    public void setOtherAttributes(String name, Object value) {
        this.otherAttributes.put(name, value);
    }
}
public static void main(String[] args) throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    Map<String, Object> otherAttributes = new HashMap<>();
    otherAttributes.put("what", "1");
    StudentTestForJsonGetSet stu = new StudentTestForJsonGetSet("1", "亚瑟", 30, otherAttributes);
    //序列化
    String stuStr = mapper.writeValueAsString(stu);
    System.out.println(stuStr);
    //反序列化
    String stuFullStr = "{\"id\":\"1\",\"studentName\":\"亚瑟\",\"age\":30,\"what\":\"1\"}";
    StudentTestForJsonGetSet stu2 = mapper.readValue(stuFullStr, StudentTestForJsonGetSet.class);
    System.out.println(stu2.toString());
}
{"id":"1","name":"亚瑟","age":30,"what":"1"}
StudentTestForJsonGetSet(id=1, name=null, age=30, otherAttributes={what=1, studentName=亚瑟})
1.5.2 嵌套的otherAttributes

如果otherAttributes接收嵌套的JSON结构,则Map的Value是LinkedHashMap类型。

先准备数据

ObjectMapper mapper = new ObjectMapper();

Map<String, StudentTestForJsonGetSet> level2 = new HashMap<>();
StudentTestForJsonGetSet studentTestForJsonGetSet = new StudentTestForJsonGetSet();
studentTestForJsonGetSet.setId("1-1");
studentTestForJsonGetSet.setName("name-1-1");
studentTestForJsonGetSet.setAge(11);
level2.put("Key-1-1", studentTestForJsonGetSet);

Map<String, Object> level1 = new HashMap<>();
level1.put("Key-1", level2);

StudentTestForJsonGetSet stu = new StudentTestForJsonGetSet("1", "亚瑟", 30, level1);

序列化

String stuStr = mapper.writeValueAsString(stu);
System.out.println(stuStr);
{"id":"1","name":"亚瑟","age":30,"Key-1":{"Key-1-1":{"id":"1-1","name":"name-1-1","age":11}}}

反序列化

String stuFullStr = "{\"id\":\"1\",\"name\":\"亚瑟\",\"age\":30,\"Key-1\":{\"Key-1-1\":{\"id\":\"1-1\",\"name\":\"name-1-1\",\"age\":11}}}";
StudentTestForJsonGetSet stu2 = mapper.readValue(stuFullStr, StudentTestForJsonGetSet.class);
System.out.println(stu2.toString());

springboot JSONObj springboot jsonobjectmapper_Jackson

2. 仅序列化时生效注解

2.1 @JsonFormat

在序列化日期/时间值时指定格式。
下面@JsonFormat注解指定时间序列化后的格式。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentTestForJsonFormat {

    private String id;

    private String name;

    private Integer age;

    //默认情况下,Date序列化为自1970年1月1日以来的毫秒数(long类型)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date time;
}

序列化:

StudentTestForJsonFormat stu = new StudentTestForJsonFormat("1", "亚瑟", 30, new Date());
String stuStr = mapper.writeValueAsString(stu);
System.out.println(stuStr);

打印序列化结果:

{"id":"1","name":"亚瑟","age":30,"time":"2022-03-28 17:21:26"}

反序列化:

String stuFullStr = "{\"id\":\"1\",\"name\":\"亚瑟\",\"age\":30,\"time\":\"2022-03-07 18:39:12\"}";
StudentTestForJsonFormat stu2 = mapper.readValue(stuFullStr, StudentTestForJsonFormat.class);
System.out.println(stu2.toString());

打印反序列化结果:

StudentTestForJsonFormat(id=1, name=亚瑟, age=30, time=Mon Mar 07 18:39:12 CST 2022)

2.2 @JsonInclude

@JsonInclude是非常重要的且常用的注解,它可以修饰在类名上或者属性上,但是一般为了更加细粒度的控制,都修饰在属性上。

类型

说明

ALWAYS

这个是默认值,无论属性值是否为空,都参加序列化

NON_NULL

属性值不是NULL,才参加序列化

NON_ABSENT

NON_NULL的增强版,Optional类型不是null,且isPresent()为true,才参加序列化。 实际开发中并不建议在实体类定义Optional类型的属性,如果你非要用,一定要赋默认值,比如Optional.empty()。

NON_EMPTY

属性值不是NULL,也不是"",如果是集合则isEmpty() = false,才参加序列化

NON_DEFAULT

属性值为缺省值时不序列化,比如int类型=0,String类型=null,这样不参加序列化。 实际开发中不要在实体类中用基础类型(如int,float),要用Integer代替int,具体原因参考阿里巴巴Java开发手册。

CUSTOM

结合注解JsonInclude.valueFilter和JsonInclude.contentFilter使用,这两个注解会指定一个Class,然后默认调用这个Class的空参构造方法,返回的对象eques属性值的话,序列化时就忽略。

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentTestForJsonInclude {

    //哪怕是null,也会参与序列化
    @JsonInclude
    private String id;

    //非NULL
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private String name;

    //非NULL,非Optional.empty()
    @JsonInclude(JsonInclude.Include.NON_ABSENT)
    private Optional<String> nickName;

    @JsonInclude(JsonInclude.Include.NON_ABSENT)
    private Optional<String> nickName2;

    //非NULL,非“”,
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private String englishName;

    //非NULL,集合isEmpty() = false
    @JsonInclude(JsonInclude.Include.NON_EMPTY)
    private List<CourseScore> courseScores;

    //属性值为缺省值时,不序列化
    @JsonInclude(JsonInclude.Include.NON_DEFAULT)
    private Integer age = 0;
}

序列化:

//注意要设置这个,用来支持Optional类型
mapper.registerModule(new Jdk8Module());

StudentTestForJsonInclude stu = new StudentTestForJsonInclude();
stu.setId(null);
stu.setName(null);
stu.setNickName(Optional.ofNullable("亚瑟"));
stu.setNickName2(Optional.empty());
stu.setEnglishName("");
stu.setCourseScores(new ArrayList<>());
stu.setAge(0);

//序列化
String stuStr = mapper.writeValueAsString(stu);
System.out.println(stuStr);

打印序列化结果:

{"id":null,"nickName":"亚瑟"}

2.3 @JsonPropertyOrder

在序列化的时候自定义属性输出顺序
在类上修饰@JsonPropertyOrder,指定序列化后的JSON字段顺序为"age", “studentName”, “id”

@Data
@AllArgsConstructor
@NoArgsConstructor
@JsonPropertyOrder(value = {"age", "studentName", "id"})
public class StudentTestForJsonPropertyOrder {

    private String id;

    @JsonProperty("studentName")
    private String name;

    private Integer age;
}

序列化:

StudentTestForJsonPropertyOrder stu = new StudentTestForJsonPropertyOrder("1", "亚瑟", 30);
String stuStr = mapper.writeValueAsString(stu);
System.out.println(stuStr);

打印序列化结果:

{"age":30,"studentName":"亚瑟","id":"1"}

2.4 @JsonView

@JsonView是Jackson的一个很实用的注解,比如一个类的对象,要根据当前登录人的权限来分别序列化成他只能看到的字段。比如数据库里一条员工信息,老板和小组长的权限不同,应该看到的数据范围也不同。

public class Views {

    public static class NameOnly{};
	
	//NameAndAge 继承了 NameOnly
    public static class NameAndAge extends NameOnly{};
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class StudentTestForJsonView {

    private String id;

    @JsonView(Views.NameOnly.class)
    private String name;

    @JsonView(Views.NameAndAge.class)
    private Integer age;
}

序列化:

StudentTestForJsonView stu = new StudentTestForJsonView("1", "亚瑟", 30);

//序列化-NameOnly
String stuNameOnly = mapper.writerWithView(Views.NameOnly.class).writeValueAsString(stu);
System.out.println(stuNameOnly);

//序列化-NameAndAge
String stuNameAndAge = mapper.writerWithView(Views.NameAndAge.class).writeValueAsString(stu);
System.out.println(stuNameAndAge);

打印序列化结果:

{"id":"1","name":"亚瑟"}
{"id":"1","name":"亚瑟","age":30}

2.5 @JsonRawValue

@JsonRawValue完全按照原样序列化属性的值

public class RawBean {
    public String name;
 
    @JsonRawValue
    public String json;
}
RawBean bean = new RawBean("My bean", "{\"attr\":false}");

打印序列化结果:

{
    "name":"My bean",
    "json":{
        "attr":false
    }
}

而不是

{
    "name":"My bean",
    "json":"{\"attr\":false}"
}

四、结语

本文总结了Jackson常用的序列化和反序列化方法,以及常用注解,但这些只是Jackson知识的很小一部分。Jackson还提供了丰富的配置项,JsonNode、ObjectNode还有JsonGenerator等功能。随着深入的学习,再一一记录。