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());
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等功能。随着深入的学习,再一一记录。