抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

一、Jackson 注解示例

1.1、简介

在本章中,我们将深入探讨Jackson注解。我们将看到如何使用现有的注解,如何创建自定义注解,最后-如何禁用它们。

1.2、maven 依赖

首先,将jackson-databind依赖项添加到pom.xml:

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<!-- 或者 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.8</version>
</dependency>

此依赖项还将在类路径中可传递地添加以下库:

  1. jackson-annotations-2.9.8.jar
  2. jackson-core-2.9.8.jar
  3. jackson-databind-2.9.8.jar

1.3、Jackson序列化注解

首先,我们来看看序列化注解。

1.3.1、@JsonAnyGetter

{@JsonAnyGetter } 注解允许灵活地使用Map字段作为标准属性。
这是一个简单的示例– ExtendableBean实体具有name属性和一组以键/值对形式的可扩展属性,我们还可以使用启用为false的可选参数来禁用@JsonAnyGetter()。 在这种情况下,Map将转换为JSON,并在序列化后显示在properties变量下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.yiyi.entity;

import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.Map;

/**
* @author wuxuan.chai
* @date 2020/6/8 9:44 上午
*/

@Getter
@Setter
@NoArgsConstructor
public class ExtendableBean {
private String name;
private Map<String,Object> properties;

private Map<String,Object> properties1;

public String getName() {
return name;
}

@JsonAnyGetter(enabled = false)
public Map<String, Object> getProperties() {
return properties;
}

@JsonAnyGetter
public Map<String,Object> getProperties1(){
return properties1;
}


public static void main(String[] args) throws JsonProcessingException {
ExtendableBean extendableBean = new ExtendableBean();
extendableBean.setName("wuxuan");
Map<String, Object> map = Maps.newHashMap();
map.put("age",11);
map.put("sex","male");
extendableBean.setProperties(map);
Map<String, Object> map1 = Maps.newHashMap();
map1.put("work","java pg");
map1.put("no",1);
extendableBean.setProperties1(map1);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(extendableBean);
System.out.println(json);

// result: {"name":"wuxuan","properties":{"sex":"male","age":11},"no":1,"work":"java pg"}
}
}

1.3.1、@JsonGetter

@JsonGetter注解是@JsonProperty注解的替代方法将方法标记为getter方法。
在以下示例中,我们将方法getTheName() 指定为MyBeanentity的name属性的getter方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package com.yiyi.entity;

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.naming.Name;

/**
* @author wuxuan.chai
* @date 2020/6/8 10:06 上午
*/

@Setter
@NoArgsConstructor
public class MyBean {
private int id;
private String name;

@JsonGetter("name")
public String getTheName(){
return this.name;
}
public int getId(){
return this.id;
}

public static void main(String[] args) throws JsonProcessingException {
MyBean myBean = new MyBean();
myBean.setId(1);
myBean.setName("wuxuan");

ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(myBean);
System.out.println(json);

//result: {"id":1,"name":"wuxuan"}

}

}

1.3.3、@JsonPropertyOrder

我们可以使用@JsonPropertyOrder批注指定序列化属性的顺序。让我们为MyBean实体的属性设置自定义顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.yiyi.entity;

import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.naming.Name;

/**
* @author wuxuan.chai
* @date 2020/6/8 10:06 上午
*/

@Setter
@NoArgsConstructor
@JsonPropertyOrder({"name","id"})
public class MyBean {
private int id;
private String name;

@JsonGetter("name")
public String getTheName(){
return this.name;
}
public int getId(){
return this.id;
}

public static void main(String[] args) throws JsonProcessingException {
MyBean myBean = new MyBean();
myBean.setId(1);
myBean.setName("wuxuan");

ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(myBean);
System.out.println(json);

//result: {"name":"wuxuan","id":1}

}

}

我们还可以使用@JsonPropertyOrder(alphabetic = true)按字母顺序对属性进行排序。 在这种情况下,序列化的输出将是:

1
{"id":1,"name":"wuxuan"}

1.3.4、@JsonRawValue

@JsonRawValue批注可以指示Jackson完全按原样序列化属性。在以下示例中,我们使用@JsonRawValue嵌入一些自定义JSON作为实体的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.yiyi.entity;

import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 10:15 上午
*/
@Getter
@Setter
public class RawBean {
public String name;

@JsonRawValue(false)
public String json;

@SneakyThrows
public static void main(String[] args) {
RawBean rawBean = new RawBean();
rawBean.setName("wuxuan");
rawBean.setJson("{\"raw\":false}");
String json = new ObjectMapper().writeValueAsString(rawBean);
System.out.println(json);
//@JsonRawValue result: {"name":"wuxuan","json":{"raw":false}}
//@JsonRawValue(false) result: {"name":"wuxuan","json":"{\"raw\":false}"}
}

}

我们还可以使用可选的布尔参数值来定义此注解是否处于活动状态。

1.3.5、@JsonValue

@JsonValue表示库将用于序列化整个实例的单个方法。例如,在一个枚举中,我们用@JsonValue注解getName,以便任何这样的实体都通过其名称序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.yiyi.entity;

import com.fasterxml.jackson.annotation.JsonValue;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 10:23 上午
*/
@AllArgsConstructor

public enum TypeEnumWithValue {

/**
* 测试枚举序列化
*/

TYPE1(1, "wuxuan"),
TYPE2(2, "yiyi");

private final Integer id;
private final String name;

public Integer getId(){
return id;
}

@JsonValue
public String getName(){
return name;
}

@SneakyThrows
public static void main(String[] args) {
String json = new ObjectMapper().writeValueAsString(TYPE1);
System.out.println(json);

//result: "wuxuan"
}
}

1.3.6、@JsonRootName

如果启用了包装,则使用@JsonRootName批注指定要使用的根包装的名称。包装意味着不要将用户序列化为以下内容:

1
2
3
4
{
"id": 1,
"name": "wuxuan"
}

而是序列化为:

1
2
3
4
5
6
{
"user": {
"id": 1,
"name": "wuxuan"
}
}

因此,让我们看一个示例–我们将使用@JsonRootName批注指示此潜在包装实体的名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.yiyi.entity;

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 10:30 上午
*/
@JsonRootName("user")
@Getter
@Setter
public class UserWithRoot {
private Integer id;
private String name;

@SneakyThrows
public static void main(String[] args) {
UserWithRoot userWithRoot = new UserWithRoot();
userWithRoot.setId(1);
userWithRoot.setName("wuxuan");

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String json = objectMapper.writeValueAsString(userWithRoot);
System.out.println(json);
//result: {"user":{"id":1,"name":"wuxuan"}}
}

}

默认情况下,包装器的名称将为类– UserWithRoot。 通过使用注解,我们得到了看上去更干净的用户:

1
2
3
4
5
6
{
"user": {
"id": 1,
"name": "wuxuan"
}
}

从Jackson 2.4开始,新的可选参数名称空间可用于XML之类的数据格式。 如果添加它,它将成为标准名称的一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.yiyi.entity;

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 10:30 上午
*/
@JsonRootName(value = "user",namespace = "users")
@Getter
@Setter
public class UserWithRoot {
private Integer id;
private String name;

@SneakyThrows
public static void main(String[] args) {
UserWithRoot userWithRoot = new UserWithRoot();
userWithRoot.setId(1);
userWithRoot.setName("wuxuan");
XmlMapper xmlMapper = new XmlMapper();
xmlMapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
String xml = xmlMapper.writeValueAsString(userWithRoot);
System.out.println(xml);

// result:
// <user xmlns="users">
// <id xmlns="">1</id>
// <name xmlns="">wuxuan</name>
// </user>
}

}

序列化的xmlMapper结果为:

1
2
3
4
<user xmlns="users">
<id xmlns="">1</id>
<name xmlns="">wuxuan</name>
</user>

1.3.6、@JsonSerialize

@JsonSerialize表示在编组实体时要使用的自定义序列化程序。让我们看一个简单的例子。 我们将使用@JsonSerialize通过CustomDateSerializer序列化eventDate属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.yiyi.entity;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yiyi.annotation.support.CustomDateSerializer;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @author wuxuan.chai
* @date 2020/6/8 10:45 上午
*/

@Getter
@Setter
public class Event {
public String name;

@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;

@SneakyThrows
public static void main(String[] args) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyy hh:mm:ss");

Event event = new Event();
event.setName("wuxuan coming");
Date eventDate = new Date();
event.setEventDate(eventDate);
System.out.println(simpleDateFormat.format(eventDate));

String json = new ObjectMapper().writeValueAsString(event);
System.out.println(json);

//result: 08-06-2020 10:54:12
//result: {"name":"wuxuan coming","eventDate":"08-06-2020 10:52:37"}
}
}

1.4、Jackson 反序列化的注解

接下来,开始探索Jackson的反序列化的注解

1.4.1、@JsonCreator

我们可以使用@JsonCreator批注来调整反序列化中使用的构造函数/工厂。 当我们需要反序列化某些与我们需要获取的目标实体不完全匹配的JSON时,这非常有用。
看下面的例子,我们需要反序列化这个json:

1
2
3
4
{
"id": 1,
"the name": "wuxuan"
}

但是,我们的目标实体中没有TheName字段-只有一个name字段。 现在,我们不想更改实体本身-我们只需要对解组过程进行更多控制-通过使用@JsonCreator注解构造函数并同时使用@JsonProperty注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.yiyi.annotation.deseizlize;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.ToString;

/**
* @author wuxuan.chai
* @date 2020/6/8 11:04 上午
*/
@Getter
@Setter
@ToString
public class BeanWithCreator {
private int id;
private String name;


public BeanWithCreator(@JsonProperty("id") int id,@JsonProperty("the name") String name) {
this.id = id;
this.name = name;
}

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
"\"id\": 1,\n" +
"\"the name\": \"wuxuan\"\n" +
"}";

BeanWithCreator value = new ObjectMapper().readerFor(BeanWithCreator.class).readValue(json);
System.out.println(value.toString());

//result: BeanWithCreator(id=1, name=wuxuan)

}
}

1.4.2. @JacksonInject

@JacksonInject指示属性将从注入而不是从JSON数据获取其值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.yiyi.annotation.deseizlize;

import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.databind.InjectableValues;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 11:15 上午
*/
@Getter
@Setter
public class BeanWithInject {
@JacksonInject
private int id;

private String name;

@SneakyThrows
public static void main(String[] args) {
String json = "{\"name\": \"wuxuan\"}";
InjectableValues.Std std = new InjectableValues.Std().addValue(int.class, 1);
BeanWithInject beanWithInject = new ObjectMapper().reader(std).forType(BeanWithInject.class).readValue(json);
System.out.println(beanWithInject.id == 1);
//result: true
}

}

1.4.3. @JsonAnySetter

@JsonAnySetter使我们可以灵活地使用Map作为标准属性。 反序列化时,JSON的属性将被简单地添加到Map中。让我们看看它是如何工作的–我们将使用@JsonAnySetter反序列化实体ExtendableBean,将下列的json反序列化:

1
2
3
4
5
{
"name":"My bean",
"attr2":"val2",
"attr1":"val1"
}

如何使用@JsonAnySetter注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.yiyi.annotation.deseizlize;

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.ToString;

import java.util.HashMap;
import java.util.Map;

/**
* @author wuxuan.chai
* @date 2020/6/8 11:21 上午
*/
@Getter
@Setter
@ToString
public class ExtendableBean {
private String name;
private Map<String,Object> properties = new HashMap<>();

@JsonAnySetter
public void setProperties(String key,String value){
properties.put(key,value);
}

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"name\":\"My bean\",\n" +
" \"attr2\":\"val2\",\n" +
" \"attr1\":\"val1\"\n" +
"}";

ExtendableBean extendableBean = new ObjectMapper().readerFor(ExtendableBean.class).readValue(json);
System.out.println(extendableBean.toString());

//result: ExtendableBean(name=My bean, properties={attr2=val2, attr1=val1})
}
}

1.4.4. @JsonSetter

@JsonSetter是@JsonProperty的替代方法––将方法标记为设置方法。
当我们需要读取一些JSON数据但目标实体类与数据不完全匹配时,这非常有用,因此我们需要调整过程以使其适合。在以下示例中,我们将在MyBeanentity中将setTheName()方法指定为name属性的设置方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.yiyi.annotation.deseizlize;

import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 11:36 上午
*/
@Getter
public class MyBean {
@Setter
private int id;
private String theName;

@JsonSetter("name")
public void setTheName(String name){
this.theName = name;
}

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"id\": 1,\n" +
" \"name\": \"wuxuan\"\n" +
"}";

MyBean myBean = new ObjectMapper().readerFor(MyBean.class).readValue(json);
System.out.println(myBean.getTheName().equals("wuxuan"));
}
}

1.4.5. @JsonDeserialize

@JsonDeserialize指示使用自定义解串器。
让我们看一下效果如何–我们将使用@JsonDeserialize通过CustomDateDeserializer反序列化eventDate属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.yiyi.annotation.deseizlize;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.yiyi.annotation.support.CustomDateDeserializer;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @author wuxuan.chai
* @date 2020/6/8 11:41 上午
*/
@Getter
@Setter
public class Event {
private String name;
@JsonDeserialize(using = CustomDateDeserializer.class)
private Date eventDate;


@SneakyThrows
public static void main(String[] args) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyy hh:mm:ss");
Date parse = simpleDateFormat.parse("08-06-2020 10:52:37");
String json = "{\"name\":\"wuxuan coming\",\"eventDate\":\"08-06-2020 10:52:37\"}";
Event event = new ObjectMapper().readerFor(Event.class)
.readValue(json);
System.out.println(parse.equals(event.eventDate));

//result: true
}
}

1.4.6. @JsonAlias

@JsonAlias在反序列化期间为属性定义一个或多个备用名称。 让我们用一个简单的例子来看看这个注解是如何工作的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.yiyi.annotation.deseizlize;

import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 11:52 上午
*/
@Getter
@Setter
public class AliasBean {
@JsonAlias({"f_name","fName"})
private String firstName;

private String secondName;

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"f_name\": \"chai\",\n" +
" \"secondName\": \"wuxuan\"\n" +
"}";

AliasBean aliasBean= new ObjectMapper().readerFor(AliasBean.class).readValue(json);
System.out.println(aliasBean.getFirstName().equals("chai"));

//result: true
}
}

1.5、Jackson多态类型处理注解

接下来–让我们看一下Jackson的多态类型处理注解:

  • @JsonTypeInfo - 指示要在序列化中包含哪些类型信息的详细信息
  • @JsonSubTypes - 指示带注解类型的子类型
  • @JsonTypeName - 定义用于注解类的逻辑类型名称
    让我们看一个更复杂的示例,并使用全部三个@ JsonTypeInfo,@ JsonSubTypes和@JsonTypeName来序列化/反序列化实体Zoo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

package com.yiyi.annotation.polymorphic;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 12:54 下午
*/
@Getter
@Setter
public class Zoo {
public Animal animal;

@SneakyThrows
public static void main(String[] args) {
Dog dog = new Dog();
dog.setName("wangwang");
dog.setBackVolume(2.0);
Zoo zoo = new Zoo();
zoo.setAnimal(dog);
String json = new ObjectMapper().writeValueAsString(zoo);
System.out.println(json);

//result: {"animal":{"type":"dog","name":"wangwang","backVolume":2.0}}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.yiyi.annotation.polymorphic;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import lombok.Getter;
import lombok.Setter;

/**
* @author wuxuan.chai
* @date 2020/6/8 12:54 下午
*/
@Getter
@Setter
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonSubTypes({@JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat")})
public class Animal {
private String name;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.yiyi.annotation.polymorphic;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Getter;
import lombok.Setter;

/**
* @author wuxuan.chai
* @date 2020/6/8 12:54 下午
*/
@Getter
@Setter
@JsonTypeName("dog")
public class Dog extends Animal {
public double backVolume;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.yiyi.annotation.polymorphic;

import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Getter;
import lombok.Setter;

/**
* @author wuxuan.chai
* @date 2020/6/8 12:56 下午
*/
@Getter
@Setter
@JsonTypeName("cat")
public class Cat extends Animal{
private boolean likesCream;
private int lives;
}

现在测试反序列化:

1
2
3
4
5
6
7
8
{
"animal":
{
"type":"dog",
"name":"wangwang",
"backVolume":2.0
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.yiyi.annotation.polymorphic;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import lombok.ToString;

/**
* @author wuxuan.chai
* @date 2020/6/8 12:54 下午
*/
@Getter
@Setter
@ToString
public class Zoo {
public Animal animal;

@SneakyThrows
public static void main(String[] args) {
String dJson = "{\"animal\":{\"type\":\"dog\",\"name\":\"wangwang\",\"backVolume\":2.0}}";

Zoo zoo1 = new ObjectMapper().readerFor(Zoo.class).readValue(dJson);
System.out.println(zoo1);

//result: Zoo(animal=Dog(backVolume=2.0))
}
}

1.6、Jackson一般的注解

接下来,一起讨论一些jackson的一般注解

1.6.1、@JsonProperty

我们可以添加@JsonProperty注解去表示在json中的属性名称,在处理非标准的getter和setter时,让我们使用@JsonProperty对属性名称进行序列化/反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 1:47 下午
*/

public class MyBean {
@Getter
@Setter
private Integer id;
private String theName;

@JsonProperty("name")
public void setTheName(String name) {
this.theName = name;
}

@JsonProperty("name")
public String getTheName() {
return theName;
}


@SneakyThrows
public static void main(String[] args) {
MyBean myBean = new MyBean();
myBean.setId(1);
myBean.setTheName("wuxuan");
String json = new ObjectMapper().writeValueAsString(myBean);
System.out.println(json);
MyBean myBean1 = new ObjectMapper().readerFor(MyBean.class).readValue(json);
System.out.println(myBean1.theName.equals(myBean.theName));

//reasult:

//{"id":1,"name":"wuxuan"}
//true
}
}

1.6.2、@JsonFormat

@JsonFormat批注指定序列化日期/时间值时的格式。在下面的示例中,我们使用@JsonFormat来控制属性eventDate的格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.*;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @author wuxuan.chai
* @date 2020/6/8 1:53 下午
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Event {
private String name;

@JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "dd-MM-yyyy hh:mm:ss")
private Date eventDate;

@SneakyThrows
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String format = simpleDateFormat.format(date);
Event event = new Event("wuxuan comming", date);
String json = new ObjectMapper().writeValueAsString(event);
assert json.contains(format);

}
}

1.6.3、@JsonUnWrapped

@JsonUnwrapped定义应展开/展平的值序列化/反序列化时。让我们看看它是如何工作的; 我们将使用注解解开属性名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:01 下午
*/
@Data
public class UnwappedUser {
private int id;

@JsonUnwrapped(prefix = "n_",suffix = "_o",enabled = true)
private Name name;

@Data
public static class Name{
private String firstName;
private String lastName;
}

@SneakyThrows
public static void main(String[] args) {
Name name = new Name();
name.setFirstName("chai");
name.setLastName("wuxuan");
UnwappedUser unwappedUser = new UnwappedUser();
unwappedUser.setName(name);

String json = new ObjectMapper().writeValueAsString(unwappedUser);
System.out.println(json);

//result: {"id":0,"n_firstName_o":"chai","n_lastName_o":"wuxuan"}
}
}

1.6.4、@JsonView

@JsonView指示将在其中包含属性以进行序列化/反序列化的View。一个示例将确切显示其工作原理-我们将使用@JsonView序列化Item实体的实例。 让我们从视图开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:08 下午
*/
@Data
@AllArgsConstructor
public class Item {
@JsonView(Views.Public.class)
private Integer id;
@JsonView(Views.Public.class)
private String itemName;
@JsonView(Views.internal.class)
private String ownerName;

@SneakyThrows
public static void main(String[] args) {
Item item = new Item(1, "wuxuan", "wuxuan111");
String json = new ObjectMapper().writerWithView(Views.Public.class).writeValueAsString(item);
System.out.println(json);
}
}

1.6.5. @JsonManagedReference, @JsonBackReference

@JsonManagedReference和@JsonBackReference注解可以处理父/子关系并解决循环。在以下示例中–我们使用@JsonManagedReference和@JsonBackReference序列化我们的ItemWithRefentity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.SneakyThrows;

import java.util.List;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:18 下午
*/
@Data
@AllArgsConstructor
public class ItemWithRef {
private int id;
private String itemName;

@JsonManagedReference
public UserWithRef owner;


@SneakyThrows
public static void main(String[] args) {
UserWithRef userWithRef = new UserWithRef(1,"wuxuan",null);
ItemWithRef itemWithRef = new ItemWithRef(1, "wwww", null);
List<ItemWithRef> itemWithRefs = Lists.newArrayList();
itemWithRefs.add(itemWithRef);
userWithRef.setUserItems(itemWithRefs);
String json = new ObjectMapper().writeValueAsString(userWithRef);
System.out.println(json);
//result: {"id":1,"name":"wuxuan"}
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import lombok.Data;

import java.util.List;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:19 下午
*/
@Data
@AllArgsConstructor
public class UserWithRef {
public int id;
private String name;

@JsonBackReference
public List<ItemWithRef> userItems;

public List<ItemWithRef> addItem(ItemWithRef itemWithRef) {
if (userItems == null){
userItems = Lists.newArrayList();
userItems.add(itemWithRef);
return userItems;
}
userItems.add(itemWithRef);
return userItems;
}
}

处理循环调用的场景

1.6.6. @JsonIdentityInfo

@JsonIdentityInfo指示在对值进行序列化/反序列化时应使用对象标识,例如,以处理无限递归类型的问题。在以下示例中–我们有一个ItemWithIdentity实体,它与UserWithIdentity实体具有双向关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.google.common.collect.Lists;
import lombok.Data;

import java.util.List;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:30 下午
*/
@Data
@JsonIdentityInfo(
generator= ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
public class UserWithIdentity {
private Integer id;
private String name;
private List<ItemWithIdentity> userItems;

public UserWithIdentity(Integer id, String name) {
this.id = id;
this.name = name;
}

public void addItem(ItemWithIdentity item) {
if (userItems == null){
userItems = Lists.newArrayList();
userItems.add(item);
return;
}
userItems.add(item);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:29 下午
*/
@JsonIdentityInfo(
generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id"
)
@Data
public class ItemWithIdentity {
private Integer id;
private String itemName;

private UserWithIdentity owner;

public ItemWithIdentity(Integer id, String itemName, UserWithIdentity owner) {
this.id = id;
this.itemName = itemName;
this.owner = owner;
}

@SneakyThrows
public static void main(String[] args) {
UserWithIdentity user = new UserWithIdentity(1, "John");
ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
user.addItem(item);
String json = new ObjectMapper().writeValueAsString(item);
System.out.println(json);

//result: {"id":2,"itemName":"book","owner":{"id":1,"name":"John","userItems":[2]}}
}

}

1.6.7. @JsonFilter

@JsonFilter批注指定在序列化期间使用的过滤器。让我们看一个例子; 首先,我们定义实体,然后指向过滤器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.yiyi.annotation.generate;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:47 下午
*/
@JsonFilter("myFilter")
@Data
public class BeanWithFilter {

private Integer id;

private String name;

public BeanWithFilter(Integer id, String name) {
this.id = id;
this.name = name;
}

@SneakyThrows
public static void main(String[] args) {
BeanWithFilter beanWithFilter = new BeanWithFilter(1, "Wuxuuan");
String json = new ObjectMapper()
.writer(new SimpleFilterProvider()
.addFilter("myFilter", SimpleBeanPropertyFilter.filterOutAllExcept("name")))
.writeValueAsString(beanWithFilter);

System.out.println(json);

//result: {"name":"Wuxuuan"}

}
}

1.7、自定义Jackson注解

接下来,我们一起学习如何创建一个Jackson自定义注解,我们可以使用@JacksonAnnotationsInside注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.yiyi.annotation.customAnnotation;

import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:54 下午
*
* 运行时注解
* json包含非空
* json排序
*/
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"name","id","datteCreated"})
public @interface CustomAnnotation {
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.yiyi.annotation.customAnnotation;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

import java.util.Date;

/**
* @author wuxuan.chai
* @date 2020/6/8 2:56 下午
*/
@CustomAnnotation
@Data
public class BeanWithCustomAnnotation {
private Integer id;
private String name;
private Date dateCreated;

public BeanWithCustomAnnotation(Integer id, String name, Date dateCreated) {
this.id = id;
this.name = name;
this.dateCreated = dateCreated;
}

@SneakyThrows
public static void main(String[] args) {
BeanWithCustomAnnotation bean = new BeanWithCustomAnnotation(1, "wuxuan", new Date());
String json = new ObjectMapper().writeValueAsString(bean);
System.out.println(json);

//result:{"name":"wuxuan","id":1,"dateCreated":1591599545147}
}
}

1.8、Jackson 混入注解

接下来,让我们一起看看如何使用Jackson的混入注解,让我们用混入注解举个例子 - 忽略User类中的属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.yiyi.annotation.mixln;

import lombok.Data;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:05 下午
*/
@Data
public class User {
private String sex;
}

1
2
3
4
5
6
7
8
9
10
11
12
package com.yiyi.annotation.mixln;

import com.fasterxml.jackson.annotation.JsonIgnoreType;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:06 下午
*/
@JsonIgnoreType
public class MyMixinForIgnoreType {
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.yiyi.annotation.mixln;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:04 下午
*/
@Data
public class Item {
private Integer id;
private String itemName;
private User owner;

public Item(Integer id, String itemName, User owner) {
this.id = id;
this.itemName = itemName;
this.owner = owner;
}

@SneakyThrows
public static void main(String[] args) {
Item item = new Item(1, "ccc", null);
String json = new ObjectMapper().writeValueAsString(item);
//检查是否序列化了User类型
assert json.contains("owner");

ObjectMapper objectMapper = new ObjectMapper().addMixIn(User.class, MyMixinForIgnoreType.class);
String json1 = objectMapper.writeValueAsString(item);

//检查是否忽略了User类型
assert !json1.contains("owner");
}
}

1.9、禁用 Jackson 注解

最后,我们一起看看如何禁用所有的jackson注解,我们可以通过禁用MapperFeature.USE_ANNOTATIONS来实现此目的,如以下示例所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.yiyi.annotation.disabledJson;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:13 下午
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({"name","id"})
public class MyBean {
private String id;
private String name;


public MyBean(String id, String name) {
this.id = id;
this.name = name;
}

@SneakyThrows
public static void main(String[] args) {
MyBean wuxuan = new MyBean("1", "wuxuan");
String json = new ObjectMapper().disable(MapperFeature.USE_ANNOTATIONS).writeValueAsString(wuxuan);
System.out.println(json);

//期望,非空校验和排序注解都失效
//result:{"id":"1","name":"wuxuan"}
}
}

二、Jackson ObjectMapper 的简介

2.1、简介

本章着重于理解Jackson ObjectMapper类,以及如何将Java对象序列化为JSON以及如何将JSON字符串反序列化为Java对象。

2.2、使用ObjectMapper读和写

让我们从基本的读写操作开始。ObjectMapper的简单readValue API是一个很好的入口点。 我们可以使用它来将JSON内容解析或反序列化为Java对象。同样,在编写方面,我们可以使用writeValue API将任何Java对象序列化为JSON输出。在本章中,我们将使用以下带有两个字段的Car类作为对象进行序列化或反序列化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.yiyi.objectMapper.using;

import lombok.Data;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:23 下午
*/
@Data
public class Car {
private String color;
private String type;
}

2.2.1、Java对象序列化成json

让我们一起学习第一个序列化例子,用ObjectMapper类的writeValue将java转换成json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.yiyi.objectMapper.using;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

import java.io.File;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:23 下午
*/
@Data
public class Car {
private String color;
private String type;

public Car(String color, String type) {
this.color = color;
this.type = type;
}

@SneakyThrows
public static void main(String[] args) {
Car car = new Car("red", "BMW");
new ObjectMapper().writeValue(new File("/Users/wuxuan.chai/Documents/project/springboot-learn/Jackson/src/main/java/com/yiyi/objectMapper/using/Car.json"),car);
}
}

运行结果:

1
2
3
4
生成一个Car.json文件
内容:
{"color":"red","type":"BMW"}

ObjectMapper类的writeValueAsString和writeValueAsBytes方法从Java对象生成JSON,并以字符串或字节数组形式返回生成的JSON:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.yiyi.objectMapper.using;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

import java.io.File;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:23 下午
*/
@Data
public class Car {
private String color;
private String type;

public Car(String color, String type) {
this.color = color;
this.type = type;
}

@SneakyThrows
public static void main(String[] args) {
Car car = new Car("red", "BMW");
String json = new ObjectMapper().writeValueAsString(car);
System.out.println(json);
byte[] bytes = new ObjectMapper().writeValueAsBytes(car);
String bytesStr = new String(bytes);
System.out.println(json.equals(bytesStr));

}
}

运行结果:

1
2
{"color":"red","type":"BMW"}
true

2.2.2、Json转换成Java对象

以下是使用ObjectMapper类将JSON字符串转换为Java对象的简单示例:

1
{"color":"red","type":"BMW"}

readValue()函数还接受其他形式的输入,例如包含JSON字符串的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.yiyi.objectMapper.using;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;

import java.io.File;
import java.net.URL;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:23 下午
*/
@Data
@EqualsAndHashCode
public class Car {
private String color;
private String type;

public Car(String color, String type) {
this.color = color;
this.type = type;
}

public Car(){

}

@SneakyThrows
public static void main(String[] args) {
Car car = new Car("red", "BMW");

String json = "{\"color\":\"red\",\"type\":\"BMW\"}";
Car car1 = new ObjectMapper().readValue(json, Car.class);
System.out.println(car1.equals(car));

Car car2 = new ObjectMapper().readValue(new File("/Users/wuxuan.chai/Documents/project/springboot-learn/Jackson/src/main/java/com/yiyi/objectMapper/using/Car.json"), Car.class);
System.out.println(car2.equals(car));
//result:
// true
// true

}

}

2.2.3、Json转换成Jackson的Json节点

另外,可以将JSON解析为JsonNode对象,并用于从特定节点检索数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.yiyi.objectMapper.using;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;

import java.io.File;
import java.net.URL;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:23 下午
*/
@Data
@EqualsAndHashCode
public class Car {
private String color;
private String type;

public Car(String color, String type) {
this.color = color;
this.type = type;
}

public Car(){

}

@SneakyThrows
public static void main(String[] args) {
String json = "{\"color\":\"red\",\"type\":\"BMW\"}";
JsonNode jsonNode = new ObjectMapper().readTree(json);
String type = jsonNode.get("type").asText();
System.out.println(type.equals("BMW"));

//result: true
}

}

2.2.4、从Json数组字符串中创建Java集合

我们可以使用TypeReference将数组形式的JSON解析为Java对象列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.yiyi.objectMapper.using;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.ToString;

import java.util.List;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:23 下午
*/
@Data
@EqualsAndHashCode
@ToString
public class Car {
private String color;
private String type;

public Car(String color, String type) {
this.color = color;
this.type = type;
}

public Car(){

}

@SneakyThrows
public static void main(String[] args) {
String jsonArray = "[\n" +
" {\n" +
" \"color\": \"Red\",\n" +
" \"type\": \"BMW\"\n" +
" },\n" +
" {\n" +
" \"color\": \"Black\",\n" +
" \"type\": \"audi\"\n" +
" }\n" +
"]";
List<Car> cars = new ObjectMapper().readValue(jsonArray,
new TypeReference<List<Car>>(){});
System.out.println(cars);
//result: [Car(color=Red, type=BMW), Car(color=Black, type=audi)]

}

}

2.2.5、从json中创建Java Map

同样,我们可以将JSON解析为Java Map:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.yiyi.objectMapper.using;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.ToString;

import java.util.List;
import java.util.Map;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:23 下午
*/
@Data
@EqualsAndHashCode
@ToString
public class Car {
private String color;
private String type;

public Car(String color, String type) {
this.color = color;
this.type = type;
}

public Car(){

}

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"color\": \"Red\",\n" +
" \"type\": \"BMW\"\n" +
"}";
Map<String, String> javaMap = new ObjectMapper().readValue(json, new TypeReference<Map<String, String>>() {});

System.out.println(javaMap);

//result: {color=Red, type=BMW}
}

}

三、高级功能

Jackson库的最大优势之一是高度可定制的序列化和反序列化过程。在本部分中,我们将介绍一些高级功能,其中输入或输出JSON响应可能不同于生成或使用响应的对象。

3.1、 配置 Serialization / Deserialization 功能

在将JSON对象转换为Java类时,如果JSON字符串具有一些新字段,则默认过程将导致异常:

1
2
3
4
5
{
"color": "Red",
"type": "BMW",
"year": "2020"
}

上面示例中的默认解析过程中,针对Car类的Java对象的JSON字符串将导致UnrecognizedPropertyException异常。通过configure方法,我们可以扩展默认过程以忽略新字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.yiyi.advanced;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.ToString;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:59 下午
*/
@Data
@ToString
@EqualsAndHashCode
public class Car {
private String type;
private String color;

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"color\": \"Red\",\n" +
" \"type\": \"BMW\",\n" +
" \"year\": \"2020\"\n" +
" }";


// 也可以这么写: new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
ObjectMapper objectMapper = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
Car car = objectMapper.readValue(json, Car.class);
System.out.println(car);
JsonNode jsonNode = objectMapper.readTree(json);
String year = jsonNode.get("year").asText();
System.out.println(year);

}
}

对于DeserializationFeature枚举中中的设置的描述:

配置 默认值 作用描述
USE_BIG_DECIMAL_FOR_FLOATS false 如果只有通用类型描述(Object或Number或未类型化的java.util.Map或java.util.Collection上下文中)可用,此功能确定是否将JSON浮点数反序列化为java.math.BigDecimals。 如果启用,则将这些值反序列化为java.math.BigDecimals;。 如果禁用,将反序列化为Doubles。默认情况下,此功能为禁用状态,这意味着默认情况下,“未类型化”的浮点数将反序列化为Doubles(出于性能考虑,选择-BigDecimals慢于Doubles)。
USE_BIG_INTEGER_FOR_INTS false 确定是否将JSON整数(非浮点数)数字反序列化为java.math.BigIntegers的功能(如果只有通用类型描述(Object或Number或在无类型的java.util.Map或java.util.Collection上下文中) )可用。 如果启用,则将这些值反序列化为java.math.BigIntegers;。 如果禁用,则将反序列化为“最小”可用类型,根据类型的数目,可以是Integer,Long或java.math.BigInteger。默认情况下,此功能是禁用的,这意味着默认情况下,将使用最紧凑的整数类型对“未类型化”的整数进行反序列化,以优化效率。
USE_LONG_FOR_INTS false 当目标类型被松散地键入为Object或Number(或在其中)时,用于确定如何绑定“小” JSON整数(非浮点数)(适合32位有符号整数(int))的功能 未类型化的java.util.Map或java.util.Collection上下文)。 如果启用,则这些值将反序列化为Long; 如果禁用,它们将反序列化为“最小”可用类型,整数。 此外,如果启用了该选项,则尝试绑定不适合Long的值将引发com.fasterxml.jackson.core.JsonProcessingException。注意:如果启用USE_BIG_INTEGER_FOR_INTS,它将优先于此设置,并强制对所有整数值使用java.math.BigInteger。该功能默认情况下处于禁用状态,这意味着如果值合适,默认情况下将使用Integer反序列化“无类型”整数。
USE_JAVA_ARRAY_FOR_JSON_ARRAY false 绑定“无类型”对象(标称类型为java.lang.Object的对象)时确定JSON数组是映射到Object[]还是List的功能。 如果为true,则绑定为Object[]; 如果为false,则为List。默认情况下,功能是禁用的,这意味着JSON数组绑定为java.util.Lists。
FAIL_ON_UNKNOWN_PROPERTIES true 确定遇到未知属性(不映射到属性,并且没有“任何setter”或处理程序的处理程序)的功能是否会导致失败(通过抛出JsonMappingException)。 仅在尝试了其他所有用于未知属性的处理方法并且该属性保持未处理状态后,此设置才生效。 默认情况下启用此功能(这意味着如果遇到未知属性,将抛出JsonMappingException)。
FAIL_ON_NULL_FOR_PRIMITIVES false 反序列化为Java基本类型(如“ int”或“ double”)时,确定是否遇到JSON null的错误的功能。 如果是,则抛出JsonProcessingException来表明这一点。 如果不是,则使用默认值(0表示“ int”,0.0表示double,与JVM使用的默认值相同)。默认情况下禁用此功能。
FAIL_ON_NUMBERS_FOR_ENUMS false 确定JSON整数是否是用于反序列化Java枚举值的有效值的功能。 如果设置为“ false”,则数字是可接受的,并用于映射到匹配枚举值的ordinal(); 如果为“ true”,则不允许数字,并且将引发JsonMappingException。 如果担心可能会发生从整数值到枚举的意外映射(并且枚举始终序列化为JSON字符串的情况),那么后者的行为就很有意义。默认情况下禁用此功能。
FAIL_ON_INVALID_SUBTYPE true 确定无法找到(丢失)或解决(丢失)类名称(例如,无效的类名,不可映射的id)的多态值的类型(例如,由com.fasterxml.jackson.annotation.JsonTypeInfo指示)时发生的情况的功能; 如果启用,则引发异常; 如果为false,则使用null值。默认情况下启用功能,以便因缺少类型信息或无效类型信息而引发异常。
FAIL_ON_READING_DUP_TREE_KEY false 该功能确定在将JSON内容读入树(com.fasterxml.jackson.core.TreeNode)并遇到重复键(已为JSON对象看到的属性名称)时发生什么情况。 如果启用,将抛出JsonMappingException; 如果禁用,则不会引发异常,并且新的(较新的)值将覆盖较早的值。请注意,此属性不会影响数据绑定的其他方面。 也就是说,不对POJO属性或java.util.Map键进行检测。 可以添加新功能来控制其他情况。默认情况下,功能是禁用的,因此不会引发异常。
FAIL_ON_IGNORED_PROPERTIES false 确定在输入中遇到显式标记为可忽略的属性时将发生什么的功能:如果启用了功能,则抛出JsonMappingException; 如果为false,则默默地跳过属性。 默认情况下,功能是禁用的,因此不会引发异常。
FAIL_ON_UNRESOLVED_OBJECT_IDS true 确定遇到的对象ID引用不引用具有该ID的实际对象(“未解决的对象ID”)的情况时发生的功能:抛出异常(true),或者使用空对象代替(false) 。 请注意,如果将其设置为false,则不会进行进一步处理; 具体来说,如果引用是通过setter方法定义的,则不会调用该方法。默认情况下启用此功能,以便在反序列化结束时,未知的对象ID将导致引发异常。
FAIL_ON_MISSING_CREATOR_PROPERTIES false 该功能确定一个或多个创建者属性(绑定到创建者方法(构造函数或静态工厂方法)的参数的属性)缺少要从内容绑定到的值时发生的情况。 如果启用,则此类缺少的值会导致抛出JsonMappingException并附带第一个(按索引)缺少的属性的信息。 如果禁用,并且未将属性标记为必需,则缺少的Creator属性将由反序列化器为参数类型提供的空值填充(通常对于Object类型为null,对于基元为默认值;但可通过自定义反序列化器重新定义)。注意,具有可注射的值算作“不丢失”。默认情况下,该功能为禁用状态,因此除非缺少将创建者属性值明确标记为“必需”的属性,否则不会引发任何异常。
FAIL_ON_NULL_CREATOR_PROPERTIES false 该功能可确定如果将一个或多个Creator属性(绑定到Creator方法(构造方法或静态工厂方法)的参数的属性)绑定到null值(来自JSON或作为默认值)时会发生什么。 如果要避免代码库中为null,则这很有用,如果对非强制性字段使用Java或Scala可选参数,则这特别有用。 默认情况下,该功能为禁用状态,因此除非缺少将创建者属性值明确标记为“必需”的属性,否则不会引发任何异常。
FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY true 该功能确定缺少使用com.fasterxml.jackson.annotation.JsonTypeInfo.As.EXTERNAL_PROPERTY注解的属性但用的关联类型ID时发生的情况。 如果启用,则总是在缺少属性值时(如果类型ID确实存在)抛出JsonMappingException; 如果禁用,则仅在将属性标记为“必需”时抛出异常。默认情况下启用功能,以便在缺少子类型属性时引发异常。
FAIL_ON_TRAILING_TOKENS false 绑定根值后确定数据绑定行为的功能。 如果启用了功能,则再次调用com.fasterxml.jackson.core.JsonParser.nextToken以确保找不到更多令牌(如果找到了更多令牌,则抛出com.fasterxml.jackson.databind.exc.MismatchedInputException ); 如果禁用,则不进行进一步检查。该功能也可以称为READ_FULL_STREAM,因为它可以有效地验证输入流仅包含绑定完整值所需的数据,而仅包含其他内容(可能的可忽略空格或注解,如果数据格式支持,则除外)。由于向后兼容的原因,默认情况下功能是禁用的(因此不检查可能的尾随令牌)。
WRAP_EXCEPTIONS true 确定杰克逊代码是否应捕获并包装异常(但绝不要出错!)以添加有关问题位置(在输入范围内)的附加信息的功能。 如果启用,大多数异常将被捕获并重新抛出(特别是java.io.IOExceptions可以按原样传递,因为它们被声明为可抛出)。 这很方便,因为将检查并声明所有异常,因此有更多上下文信息。 但是,有时调用应用程序可能只希望按原样传递“原始”未经检查的异常。默认情况下启用此功能。
ACCEPT_SINGLE_VALUE_AS_ARRAY false 确定强制将非数组(JSON)值强制用于Java集合(数组,java.util.Collection)类型的功能。 如果启用,集合反序列化器将尝试处理非数组值,就像它们对JSON数组具有“隐式”一样。 此功能旨在出于兼容性/互操作性原因而使用,以与在数组中只有单个元素的情况下忽略JSON数组的程序包(例如XML到JSON转换器)一起使用。默认情况下禁用此功能。
UNWRAP_SINGLE_VALUE_ARRAYS false 确定将单个值数组(在JSON中)的值强制转换为相应值类型是否可接受的功能。 这基本上与ACCEPT_SINGLE_VALUE_AS_ARRAY功能相反。 如果在数组中找到多个值,则抛出JsonMappingException。默认情况下禁用功能
UNWRAP_ROOT_VALUE false 允许“解包”根级JSON值以匹配用于序列化的SerializationFeature.WRAP_ROOT_VALUE的设置。 将验证根JSON值是一个JSON对象,并且它具有具有预期根名的单个属性。 如果不是,则抛出JsonMappingException。 否则,包装属性的值将被反序列化,就好像它是根值一样。默认情况下禁用此功能。
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT false 可以启用此功能以允许将POJO和其他结构化值(java.util.Maps,java.util.Collections)的JSON空字符串值(“”)绑定为“ null”。 如果禁用,则只能从JSONnull或JSON对象绑定标准POJO(标准含义是未定义任何自定义反序列化器或构造函数;这两种都可以添加对其他种类JSON值的支持); 如果启用,则可以将空JSON字符串等效为JSON null。注意:这不适用于标量值,例如布尔值和数字; 是否可以强制使用它们取决于MapperFeature.ALLOW_COERCION_OF_SCALARS。默认情况下禁用此功能。
ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT false 可以启用此功能,以允许将空JSON数组值(即[])绑定为POOL(以及2.9,还包含其他值)为“ null”。 如果禁用,则只能从JSONnull或JSON对象绑定标准POJO(标准含义是未定义任何自定义反序列化器或构造函数;这两种都可以添加对其他种类JSON值的支持); 如果启用,则将使用空JSON数组等效于JSON null。默认情况下禁用此功能。
ACCEPT_FLOAT_AS_INT true 确定是否强制转换为从JSON浮点数(任何带命令(.)或指数部分(e /E'))到期望的整数(intlongjava.lang .integer,java.lang.Long,java.math.BigDecimal是否允许。 如果启用,强制将截断值; 如果禁用,将抛出JsonMappingException。默认情况下启用此功能。
READ_ENUMS_USING_TO_STRING false 确定用于Enum值的标准反序列化机制的功能:如果启用,则假定Enums已使用Enum.toString()的返回值进行了序列化; 如果禁用,则假定已使用Enum.name()的返回值。注意:此功能通常应与SerializationFeature.WRITE_ENUMS_USING_TO_STRING具有相同的值。默认情况下禁用此功能。
READ_UNKNOWN_ENUM_VALUES_AS_NULL false 允许将未知的Enum值解析为空值的功能。 如果禁用,未知的Enum值将引发异常。请注意,在某些情况下,这基本上会忽略未知的Enum值; 这是java.util.EnumMap的键的键和java.util.EnumSet的值的(因为在这些情况下不接受null)。默认情况下禁用此功能。
READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE false 该功能允许忽略未知的Enum值,并通过@JsonEnumDefaultValue批注指定预定义值。 如果禁用,未知的Enum值将引发异常。 如果启用,但未指定预定义的默认Enum值,则也会引发异常。 默认情况下禁用此功能。
READ_DATE_TIMESTAMPS_AS_NANOSECONDS true 当且仅当数据类型支持这种分辨率时,此功能可控制是否应使用(启用)或不使用(禁用)纳秒级时间戳写入数字时间戳值。 只有较新的数据类型(例如Java8日期/时间)才支持这种分辨率-较旧的类型(Java8之前的java.util.Date等)和Joda则不支持-并且此设置对此类类型无效。如果禁用,则假定为标准毫秒时间戳。 这与SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS相对应。默认情况下启用此功能,以支持最准确的时间值。
ADJUST_DATES_TO_CONTEXT_TIME_ZONE true 该功能指定上下文提供的java.util.TimeZone(DeserializationContext.getTimeZone()是否应用于在反序列化时调整日期/时间值,即使值本身包含时区信息也是如此。 如果禁用,则仅当值本身不包含任何TimeZone信息时才使用它。请注意,确切的行为取决于所讨论的日期/时间类型。 特别是JDK类型的java.util.Date没有内置时区信息,因此此设置无效。 此外,尽管java.util.Calendar确实具有此信息,但是基本JDK java.text.SimpleDateFormat无法保留已解析的区域信息,因此,无论此设置如何,java.util.Calendar始终将获得上下文时区调整。考虑到上述因素,只有Joda和Java 8日期/文字数据类型的扩展模块才支持此功能。
EAGER_DESERIALIZER_FETCH true 决定ObjectReader是否应在可能的情况下热切获取必要的JsonDeserializer的功能。 如果多次使用类似配置的ObjectReader实例,则可以提高性能。 并且不会对一次性使用案例产生重大影响。请注意,通常不需要禁用此功能:仅在存在实际可察觉问题的情况下考虑。 默认情况下启用此功能。

对于SerializationFeature枚举中的设置进行描述:

|配置|默认值|作用描述|
|-|-|
|WRAP_ROOT_VALUE|false|可以启用以使根值(通常为JSON对象,但可以是任何类型)包装在单个属性JSON对象中的功能,其中键为“根名”,由注释内省者确定(对于使用@XmlRootElement的JAXB尤其如此) .name)或后备广告(非合格的类名)。 功能主要是为了与JAXB兼容。默认情况下禁用此功能。|
|INDENT_OUTPUT|false|该功能允许使用为ObjectMapper(以及从mapper创建的ObjectWriters)配置的默认漂亮打印机为基础生成器启用(或禁用)缩进。请注意,仅当未为生成器或ObjectWriter配置显式com.fasterxml.jackson.core.PrettyPrinter时,才使用默认的漂亮打印机。默认情况下禁用此功能。|
|FAIL_ON_EMPTY_BEANS|true|该功能用于确定在找不到类型的访问器时会发生什么情况(并且没有注释指示它打算被序列化)。 如果启用(默认),则将引发异常以指示这些类型为不可序列化的类型; 如果禁用,它们将被序列化为空对象,即没有任何属性。请注意,此功能仅对那些没有任何可识别注释的“空” bean(例如,@JsonSerialize)起作用的空类型:具有注释的那些不会导致抛出异常。默认情况下启用此功能。|
|FAIL_ON_SELF_REFERENCES|true|决定POJO检测到直接自我引用时会发生什么情况的功能(并且未启用对象ID处理):抛出JsonMappingException(如果为true),或者通常对引用进行处理(false)。默认情况下启用功能|
|WRAP_EXCEPTIONS|true|确定Jackson代码是否应捕获并包装异常(但绝不要出错!)以添加有关问题位置(在输入范围内)的附加信息的功能。 如果启用,大多数异常将被捕获并重新抛出(特别是java.io.IOExceptions可以按原样传递,因为它们被声明为可抛出)。 这很方便,因为将检查并声明所有异常,因此有更多上下文信息。 但是,有时调用应用程序可能只希望按原样传递“原始”未经检查的异常。默认情况下启用此功能。|
|FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS|true|确定通常具有Jackson包含的类型信息的对象与com.fasterxml.jackson.annotation.JsonUnwrapped结合使用时发生的功能。 在默认(启用)状态下,当展开的对象具有类型信息时,将引发错误。 禁用时,将解包该对象,并丢弃类型信息。默认情况下启用此功能。|
|WRITE_SELF_REFERENCES_AS_NULL|false|决定POJO检测到直接自引用时会发生什么情况的功能(并且未启用对象ID处理):如果启用,则将该引用写为null; 如果禁用,则使用默认行为(它将尝试序列化,通常会导致异常)。 但是,如果启用了FAIL_ON_SELF_REFERENCES。 此属性将被忽略。|
|CLOSE_CLOSEABLE|false|用于确定是否在序列化后调用实现java.io.Closeable的序列化根级对象的close方法(调用了ObjectMapper的writeValue()(或等效方法)的对象)的功能。 如果启用,则在序列化完成后(无论成功还是由于抛出异常而显示的错误)将调用close()。 您可以将其视为某种“最终”处理。注意:仅影响根对象的行为,而不影响从根对象可访问的其他对象。 换句话说,每个“ writeValue”调用将只进行一次调用。默认情况下禁用此功能。|
|FLUSH_AFTER_WRITE_VALUE|true|确定是否以JsonGenerator作为参数的writeValue()方法完成后是否调用JsonGenerator.flush()的功能(即不影响使用其他目标的方法); ObjectWriter中的方法相同。 这通常是有道理的; 但是在某些情况下不应该强制执行刷新:例如,在底层流进行压缩时,并且flush()导致刷新了压缩状态(某些压缩编解码器会发生这种情况)。默认情况下启用此功能。|
|WRITE_DATES_AS_TIMESTAMPS|true|确定是否将Date(和日期/时间)值(以及基于Date的东西,如java.util.Calendars)序列化为数字时间戳记(true;默认值)或其他值(通常是文本表示形式)的功能。如果使用文本表示,则实际格式取决于配置设置,包括@JsonFormat批注的按属性使用,全局配置的java.text.DateFormat。对于“经典” JDK日期类型(java.util.Date,java.util.Calendar),默认格式由com.fasterxml.jackson.databind.util.StdDateFormat提供,并且对应于格式字符串“ yyyy-MM-dd” ‘T’HH:mm:ss.SSSX”(有关字符串格式的详细信息,请参见java.text.DateFormat)。此功能是否影响其他与日期相关的类型的处理取决于这些类型的处理程序,尽管理想情况下,他们应该使用此功能注意:使用WRITE_DATE_KEYS_AS_TIMESTAMPS代替此功能来控制java.util.Map密钥是否序列化为字符串。默认情况下启用功能,因此默认情况下将日期/时间序列化为时间戳。|
|WRITE_DATE_KEYS_AS_TIMESTAMPS|false|确定是否将用作java.util.Map键的java.util.Dates(和子类型)序列化为时间戳的功能(如果没有,则序列化为文本值)。默认值为“ false”,表示将日期值映射键序列化为文本(ISO-8601)值。默认情况下禁用此功能。|
|WRITE_DATES_WITH_ZONE_ID|false|用于确定日期/日期时间值是否应该序列化的功能,以便在类型本身包含时区信息的情况下,它们包括时区id。 包含此信息可能会导致兼容性问题,因为ISO-8601规范未定义包含此类信息的格式。如果启用,则应使用Java 8 DateTimeFormatter#ISO_ZONED_DATE_TIME定义所指定的格式来包含时区ID(例如,“ 2011-12-03T10:15:30 + 01:00 [欧洲/巴黎]”)。注意:如果日期/时间值被序列化为时间戳,则该设置不相关。默认情况下,该功能处于禁用状态,因此不包含区域ID。 相反,时区偏移量用于ISO-8601兼容性(如果值中包含任何时区信息)。|
|WRITE_DURATIONS_AS_TIMESTAMPS|true|默认情况下,用于确定表示时间段(持续时间,时间段,范围)的时间值是否要使用数字(true)或文本(false)表示序列化的功能。 请注意,根据类型的不同,数字表示可能表示简单数字或数字数组。注意:使用WRITE_DATE_KEYS_AS_TIMESTAMPS来控制java.util.Map密钥是否序列化为字符串。默认情况下启用此功能,因此默认情况下将周期/持续时间序列化为时间戳。|
|WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS|false|确定char []类型如何序列化的功能:启用后,将序列化为显式JSON数组(以单字符字符串作为值); 禁用时,默认情况下将其序列化为字符串(更为紧凑)。默认情况下禁用此功能。|
|WRITE_ENUMS_USING_TO_STRING|false|确定用于Enum值的标准序列化机制的功能:如果启用,则使用Enum.toString()的返回值; 如果禁用,则使用Enum.name()的返回值。注意:此功能通常应与DeserializationFeature.READ_ENUMS_USING_TO_STRING具有相同的值。默认情况下禁用此功能。|
|WRITE_ENUMS_USING_INDEX|false|确定Java Enum值是序列化为数字(true)还是文本值(false)的功能。 如果使用文本值,则还将考虑其他设置。 如果启用此功能,则Enum.ordinal()的返回值(整数)将用作序列化。请注意,此功能的优先级高于WRITE_ENUMS_USING_TO_STRING,只有将此功能设置为false时,才考虑此功能。请注意,从2.10开始,这不适用于作为java.util.Map值的键编写的枚举,该枚举具有单独的设置WRITE_ENUM_KEYS_USING_INDEX。默认情况下禁用此功能。|
|WRITE_ENUM_KEYS_USING_INDEX|false|确定是否将用作java.util.Map密钥的{link Enum}序列化为使用Enum.ordinal()的功能。 与将枚举写入常规值时使用的WRITE_ENUMS_USING_INDEX相似。默认情况下禁用此功能。|
|WRITE_NULL_MAP_VALUES|true|确定是否要对具有空值的Map条目进行序列化(true)或不进行序列化(false)的功能。注意:与其他SerializationFeatures不同,此功能不能在每次调用的基础上动态更改,因为在构造序列化程序和属性处理程序时会考虑其作用。默认情况下启用此功能。|
|WRITE_EMPTY_JSON_ARRAYS|true|确定是否将容器属性(具有声明的Collection或数组值的POJO属性;即,产生JSON数组的事物)为空(没有元素)的属性,将被序列化为空JSON数组(true),还是从输出中取消显示(false) 。请注意,这不会更改java.util.Maps或“类似集合”类型的行为。注意:与其他SerializationFeatures不同,此功能不能在每次调用的基础上动态更改,因为在构造序列化程序和属性处理程序时会考虑其作用。默认情况下启用此功能。|
|WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED|false|为互操作性而添加的功能,以与所谓的“ BadgerFish”约定兼容。 功能确定对单个元素java.util.Collections和数组的处理:如果启用,则仅包含一个元素的java.util.Collections和数组将被序列化,就好像该元素本身已被序列化一样。启用后,带有数组的POJO通常如下所示:{“ arrayProperty”:[1]}而是序列化为{“ arrayProperty”:1}请注意,此功能与DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY相对(即,通常两者都启用,或两者都不启用)。|
|WRITE_BIGDECIMAL_AS_PLAIN|false|确定是否使用java.math.BigDecimal.toPlainString()序列化java.math.BigDecimal条目的功能,以防止使用科学计数法写入值。注意:由于此功能通常需要使用com.fasterxml.jackson.core.JsonGenerator.writeNumber(String),因此可能会导致兼容性问题,因为并非所有com.fasterxml.jackson.core.JsonGenerator实现都支持这种输出模式:通常仅文本 基于格式的格式支持它。默认情况下禁用此功能。|
|WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS|true|控制是否使用纳秒级时间戳写入数字时间戳值(启用)(禁用)的功能; 当且仅当数据类型支持这种分辨率时。 只有较新的数据类型(例如Java8日期/时间)才支持这种分辨率-较旧的类型(Java8之前的java.util.Date等)和Joda则不支持-并且此设置对此类类型无效。如果禁用,则假定为标准毫秒时间戳。 这与DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS对应。默认情况下启用此功能,以支持最准确的时间值。|
|ORDER_MAP_ENTRIES_BY_KEYS|false|确定是否在序列化之前首先按键对java.util.Map条目进行排序的功能:如果启用,则在必要时执行附加的排序步骤(对于java.util.SortedMaps不必要),如果禁用,则无需进行其他排序。默认情况下禁用此功能。|
|EAGER_SERIALIZER_FETCH|true|决定ObjectWriter是否应在可能的情况下热切获取必要的JsonSerializer的功能。 如果多次使用类似配置的ObjectWriter实例,则可以提高性能。 并且不会对一次性使用案例产生重大影响。请注意,通常不需要禁用此功能:仅在存在实际可察觉问题的情况下考虑。默认情况下启用此功能。|
|USE_EQUALITY_FOR_OBJECT_ID|false|确定是否使用对象的真实JVM级别标识(false)比较对象标识的功能; 或equals()方法。 当使用ORM库(例如Hibernate)处理数据库绑定的对象时,后期有时会很有用。 注意对象本身是实际比较的,不是对象ID。 此功能的命名有些令人困惑,因此重要的是,要为其保留身份的对象在id之上和之外都被认为是相等的(无论如何,它们总是使用相等性进行比较)。注意:由于实现功能的方式,非常重要的是,除了覆盖Object.equals以使对象匹配(被视为“相同”)之外,还必须确保Object.hashCode()被覆盖以产生 相等实例的值完全相同。默认情况下禁用此功能; 表示使用严格的身份,而不是equals()|

3.2、创建自定义的序列化/反序列化器

ObjectMapper类的另一个基本功能是能够注册自定义序列化器和反序列化器。 在输入或输出JSON响应的结构不同且必须对其进行序列化或反序列化的Java类的结构时,自定义序列化器和反序列化器非常有用。
以下是自定义JSON序列化程序的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.yiyi.advanced;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

/**
* @author wuxuan.chai
* @date 2020/6/8 4:49 下午
*/
public class CustomCarSerializer extends StdSerializer<Car> {
protected CustomCarSerializer(Class<Car> t) {
super(t);
}

public CustomCarSerializer() {
this(null);
}

@Override
public void serialize(Car car, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
gen.writeStringField("car_brand", car.getType());
gen.writeEndObject();
}
}

此自定义序列化器可以这样调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.yiyi.advanced;

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.ToString;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:59 下午
*/
@Data
@ToString
@EqualsAndHashCode
public class Car {
private String type;
private String color;

public Car(String type, String color) {
this.type = type;
this.color = color;
}

@SneakyThrows
public static void main(String[] args) {
Car car = new Car("BMW", "Red");
String json = new ObjectMapper()
.registerModule(
new SimpleModule(
"CustomCarSerializer",
new Version(1, 0, 0, null, null, null)
).addSerializer(Car.class, new CustomCarSerializer()))
.writeValueAsString(car);
System.out.println(json);
//result:{"car_brand":"BMW"}
}
}

这是自定义JSON反序列化的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package com.yiyi.advanced;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

/**
* @author wuxuan.chai
* @date 2020/6/8 4:59 下午
*/
public class CustomCarDeserializer extends StdDeserializer<Car> {

protected CustomCarDeserializer(Class<?> vc) {
super(vc);
}

public CustomCarDeserializer(){
this(null);
}

@Override
public Car deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
Car car = new Car();
ObjectCodec codec = p.getCodec();
JsonNode treeNode = codec.readTree(p);
JsonNode colorNode = treeNode.get("color");
String color = colorNode.asText();
car.setColor(color);
return car;
}
}

此自定义反序列化器可以这样调用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.yiyi.advanced;

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.ToString;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:59 下午
*/
@Data
@ToString
@EqualsAndHashCode
public class Car {
private String type;
private String color;

public Car(String type, String color) {
this.type = type;
this.color = color;
}

public Car() {
}

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"color\": \"Red\",\n" +
" \"type\": \"BMW\""+
" }";
Car car = new ObjectMapper()
.registerModule(
new SimpleModule(
"CustomCarDeserializer",
new Version(1, 0, 0, null, null, null)
).addDeserializer(Car.class, new CustomCarDeserializer()))
.readValue(json, Car.class);
System.out.println(car);
//result: Car(type=null, color=Red)
}
}

3.3、Date 格式化的处理方式

java.util.Date的默认序列化产生一个数字,即纪元时间戳(自1970年1月1日起,以UTC为单位的毫秒数)。 但这不是人类可读的,并且需要进一步转换以人类可读的格式显示。 让我们使用datePurchased属性将到目前为止使用的Car实例包装在Request类中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.yiyi.advanced;

import lombok.Data;

import java.util.Date;

/**
* @author wuxuan.chai
* @date 2020/6/8 5:12 下午
*/
@Data
public class Request {
private Car car;
private Date createDate;
}

控制日期的字符串格式,并将其设置为例如 yyyy-MM-dd HH:mm a z,请考虑以下代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.yiyi.advanced;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
* @author wuxuan.chai
* @date 2020/6/8 5:12 下午
*/
@Data
public class Request {
private Car car;
private Date createDate;

public Request() {
}

public Request(Car car, Date createDate) {
this.car = car;
this.createDate = createDate;
}

@SneakyThrows
public static void main(StrinDeserializationFeatureg[] args) {
Car car = new Car("BMW", "Red");
Request request = new Request(car,new Date());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
ObjectMapper objectMapper = new ObjectMapper().setDateFormat(simpleDateFormat);

String json = objectMapper.writeValueAsString(request);
System.out.println(json);

//result: {"car":{"type":"BMW","color":"Red"},"createDate":"2020-06-08 17:16 下午 CST"}
}
}

更多的时间格式化的方式参考:深入学习日期格式化

3.4、集合的处理方式

DeserializationFeature类提供的另一个小而有用的功能是能够根据JSON数组响应生成所需类型的集合的功能。例如,我们可以将结果生成为数组:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.yiyi.advanced;

import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.SneakyThrows;
import lombok.ToString;

/**
* @author wuxuan.chai
* @date 2020/6/8 3:59 下午
*/
@Data
@ToString
@EqualsAndHashCode
public class Car {
private String type;
private String color;

public Car(String type, String color) {
this.type = type;
this.color = color;
}

public Car() {
}

@SneakyThrows
public static void main(String[] args) {
String json = "[\n" +
" {\n" +
" \"color\":\"Red\",\n" +
" \"type\":\"BMW\"\n" +
" },\n" +
" {\n" +
" \"color\":\"Black\",\n" +
" \"type\":\"audi\"\n" +
" }\n" +
"]";
//也可以写成: objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
ObjectMapper objectMapper = new ObjectMapper().enable(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY);
Car[] cars = objectMapper.readValue(json, Car[].class);
List<Car> carList = objectMapper.readValue(json,new TypeReference<List<Car>>(){});

}
}

四、Jackson序列化是忽略属性

4.1、简介

本章将展示在使用Jackson 2.x将对象序列化为JSON时如何忽略某些字段。当Jackson默认值还不够,并且我们需要精确控制要序列化为JSON的内容时,这非常有用-并且有几种方法可以忽略属性。

4.2、在类级别上忽略字段

我们可以使用@JsonIgnoreProperties批注并按名称指定字段,从而在类级别忽略特定字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.yiyi.ignoreField;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 5:32 下午
*/
@Data
@JsonIgnoreProperties({"intValue"})
public class MyDto {
private String stringValue;
private int intValue;
private Boolean booleanValue;

public MyDto(String stringValue, int intValue, Boolean booleanValue) {
this.stringValue = stringValue;
this.intValue = intValue;
this.booleanValue = booleanValue;
}

@SneakyThrows
public static void main(String[] args) {
MyDto myDto = new MyDto("wuxuan", 12, false);
String json = new ObjectMapper().writeValueAsString(myDto);
System.out.println(json);
//result: {"stringValue":"wuxuan","booleanValue":false}
}
}

要毫无例外地忽略JSON输入中的任何未知属性,我们可以设置@JsonIgnoreProperties批注的ignoreUnknown = true。

4.3、在字段级别上忽略字段

我们也可以直接通过@JsonIgnore批注直接忽略该字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.yiyi.ignoreField;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 5:32 下午
*/
@Data
public class MyDto {
private String stringValue;
private int intValue;
@JsonIgnore
private Boolean booleanValue;

public MyDto(String stringValue, int intValue, Boolean booleanValue) {
this.stringValue = stringValue;
this.intValue = intValue;
this.booleanValue = booleanValue;
}

@SneakyThrows
public static void main(String[] args) {
MyDto myDto = new MyDto("wuxuan", 12, false);
String json = new ObjectMapper().writeValueAsString(myDto);
System.out.println(json);

//result:{"stringValue":"wuxuan","intValue":12}
}
}

4.4、通过类型忽略所有字段

最后,我们可以使用@JsonIgnoreType批注忽略指定类型的所有字段。 如果我们控制类型,那么我们可以直接注解类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.yiyi.ignoreField;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 5:42 下午
*/
@Data
public class SomeType {
private String type;
private MyDto myDto;

public SomeType(String type, MyDto myDto) {
this.type = type;
this.myDto = myDto;
}

@SneakyThrows
public static void main(String[] args) {
MyDto wuxuan = new MyDto("wuxuan", 1, false);
SomeType type = new SomeType("www", wuxuan);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(type);
System.out.println(json);
//result:{"type":"www"}
}
}

但是,我们常常无法控制类本身; 在这种情况下,我们可以充分利用Jackson的混入注解。
首先,我们为要忽略的类型定义一个MixIn,然后使用@JsonIgnoreType对其进行注解:

1
2
3
4
5
6
7
8
9
10
11
12
package com.yiyi.ignoreField;

import com.fasterxml.jackson.annotation.JsonIgnoreType;

/**
* @author wuxuan.chai
* @date 2020/6/8 5:47 下午
*/
@JsonIgnoreType
public class MyMixinForIgnoreType {
}

然后,我们在编组期间注册该mixin来替换(并忽略)所有String[]类型:

1
2
3
4
5
6
7
8
9
10
11
12
package com.yiyi.ignoreField;

import com.fasterxml.jackson.annotation.JsonIgnoreType;

/**
* @author wuxuan.chai
* @date 2020/6/8 5:47 下午
*/
@JsonIgnoreType
public class MyMixinForIgnoreType {
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.yiyi.ignoreField;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 5:57 下午
*/
@Data
public class MyDtoWithSpecialField {
private String[] stringValue;
private int intValue;
private boolean booleanValue;

public MyDtoWithSpecialField(String[] stringValue, int intValue, boolean booleanValue) {
this.stringValue = stringValue;
this.intValue = intValue;
this.booleanValue = booleanValue;
}

@SneakyThrows
public static void main(String[] args) {
MyDtoWithSpecialField myDtoWithSpecialField = new MyDtoWithSpecialField(new String[]{"1", "2"}, 1, false);
ObjectMapper objectMapper = new ObjectMapper().addMixIn(String[].class, MyMixinForIgnoreType.class);
String json = objectMapper.writeValueAsString(myDtoWithSpecialField);
System.out.println(json);

//result: {"intValue":1,"booleanValue":false}
}

}

4.5、通过过滤器忽略字段

最后,我们还可以使用过滤器来忽略Jackson中的特定字段。 首先,我们需要在Java对象上定义过滤器,去除掉name属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.yiyi.ignoreField;

import com.fasterxml.jackson.annotation.JsonFilter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 6:03 下午
*/
@JsonFilter("myFilter")
@Data
public class MyDtoFilter {
private String type;
private String name;

public MyDtoFilter(String type, String name) {
this.type = type;
this.name = name;
}

public MyDtoFilter() {
}

@SneakyThrows
public static void main(String[] args) {
MyDtoFilter myDtoFilter = new MyDtoFilter("BMW", "x5");
SimpleBeanPropertyFilter filter = SimpleBeanPropertyFilter.serializeAllExcept("name");
SimpleFilterProvider myFilter = new SimpleFilterProvider().addFilter("myFilter", filter);
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writer(myFilter).writeValueAsString(myDtoFilter);
System.out.println(json);
//result:{"type":"BMW"}
}
}

五、在Jackson中忽略值为NULL的字段

5.1、简介

本快速章节将介绍如何在序列化Java类时设置Jackson以忽略空字段。

5.2、在类上忽略NULL字段

1
2
@JsonInclude(Include.NON_NULL)
public class MyDto { ... }

或者-更细粒度上讲-在字段级级:

1
2
3
4
5
public class MyDto{
@JsonInclude(Include.NON_NULL)
private String value;
private int number;
}

4.3. 全局忽略NULL的字段

Jackson还允许在ObjectMapper上全局配置此行为:

1
2
3
4
5
6
7
8
public class MyDtoTest{
@Test
public void testNullField() {
ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(Include.NON_NULL);
...
}
}

忽略空字段是一种常见的Jackson配置,因为通常情况下,我们需要更好地控制JSON输出。 本章说明如何对类进行此操作。 但是,还有更高级的用例,例如在序列化Map时忽略空值。

六、Jackson更改字段的名称

6.1、简介

本快速章节说明了如何更改字段名称以在序列化时映射到另一个JSON属性。

6.2、更改要序列化的字段名称

以MyDto类为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.yiyi.changeFieldName;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 6:18 下午
*/
@Data
public class MyDto {
private String value;
private String name;

public MyDto(String value, String name) {
this.value = value;
this.name = name;
}

public MyDto() {
}

@JsonProperty("value_fact")
public String getValue(){
return value;
}

@SneakyThrows
public static void main(String[] args) {
MyDto myDto = new MyDto("sfsd", "www");
String json = new ObjectMapper().writeValueAsString(myDto);
System.out.println(json);

//result: {"name":"www","value_fact":"sfsd"}
}
}

七、Jackson用未知属性反序列化JSON

7.1、简介

在本章中,我们将研究Jackson 2.x的解组过程,特别是如何处理具有未知属性的JSON内容。

7.2、使用其他/未知字段反序列化JSON

JSON输入有各种形态和大小-在大多数情况下,我们需要将其映射到具有一定数量字段的预定义Java对象。 目标是简单地忽略所有无法映射到现有Java字段的JSON属性。
例如,假设我们需要将JSON解组到以下Java实体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.yiyi.unmarshall;

import lombok.Data;

/**
* @author wuxuan.chai
* @date 2020/6/8 6:25 下午
*/
@Data
public class MyDto {
private String stringValue;
private int intValue;
private boolean booleanValue;

public MyDto() {
}

public MyDto(String stringValue, int intValue, boolean booleanValue) {
this.stringValue = stringValue;
this.intValue = intValue;
this.booleanValue = booleanValue;
}
}

7.3、未知字段上的UnrecognizedPropertyException

尝试将具有未知属性的JSON反序列化到此简单Java实体将导致com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.yiyi.unmarshall;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;

/**
* @author wuxuan.chai
* @date 2020/6/8 6:25 下午
*/
@Data
public class MyDto {
private String stringValue;
private int intValue;
private boolean booleanValue;

public MyDto() {
}

public MyDto(String stringValue, int intValue, boolean booleanValue) {
this.stringValue = stringValue;
this.intValue = intValue;
this.booleanValue = booleanValue;
}

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"stringValue\": \"wuxuan\",\n" +
" \"intValue\": 1,\n" +
" \"booleanValue111\": false\n" +
"}";

ObjectMapper objectMapper = new ObjectMapper();
MyDto myDto = objectMapper.readValue(json, MyDto.class);
//throw UnrecognizedPropertyException 异常
/**
* Unrecognized field "booleanValue111" (class com.yiyi.unmarshall.MyDto), not marked as ignorable (3 known properties: "stringValue", "booleanValue", "intValue"])
* at [Source: (String)"{
* "stringValue": "wuxuan",
* "intValue": 1,
* "booleanValue111": false
* }"; line: 4, column: 29] (through reference chain: com.yiyi.unmarshall.MyDto["booleanValue111"])
*/
}
}

7.4、通过设置ObjectMapper处理未知字段的异常

现在,我们可以配置完整的ObjectMapper以忽略JSON中的未知属性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.yiyi.unmarshall;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.ToString;

/**
* @author wuxuan.chai
* @date 2020/6/8 6:25 下午
*/
@Data
@ToString
public class MyDto {
private String stringValue;
private int intValue;
private boolean booleanValue;

public MyDto() {
}

public MyDto(String stringValue, int intValue, boolean booleanValue) {
this.stringValue = stringValue;
this.intValue = intValue;
this.booleanValue = booleanValue;
}

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"stringValue\": \"wuxuan\",\n" +
" \"intValue\": 1,\n" +
" \"booleanValue111\": true\n" +
"}";

ObjectMapper objectMapper = new ObjectMapper();

//也可以写成:objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
MyDto myDto = objectMapper.readValue(json, MyDto.class);
System.out.println(myDto);

//result: MyDto(stringValue=wuxuan, intValue=1, booleanValue=false)
// booleanValue=false 是对象的属性的默认值,因为booleanValue 是基本类型,不是包装类型,默认值为false

}
}

7.5、通过类上注解处理未知字段

我们还可以将单个类标记为接受未知字段,而不是整个Jackson对象映射器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.yiyi.unmarshall;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.ToString;

/**
* @author wuxuan.chai
* @date 2020/6/8 6:25 下午
*/
@Data
@ToString
@JsonIgnoreProperties(ignoreUnknown = true)
public class MyDto {
private String stringValue;
private int intValue;
private boolean booleanValue;

public MyDto() {
}

public MyDto(String stringValue, int intValue, boolean booleanValue) {
this.stringValue = stringValue;
this.intValue = intValue;
this.booleanValue = booleanValue;
}

@SneakyThrows
public static void main(String[] args) {
String json = "{\n" +
" \"stringValue\": \"wuxuan\",\n" +
" \"intValue\": 1,\n" +
" \"booleanValue111\": true\n" +
"}";

ObjectMapper objectMapper = new ObjectMapper();

MyDto myDto = objectMapper.readValue(json, MyDto.class);
System.out.println(myDto);
//result: MyDto(stringValue=wuxuan, intValue=1, booleanValue=false)
// booleanValue=false 是对象的属性的默认值,因为booleanValue 是基本类型,不是包装类型,默认值为false

}
}

八、总结

初次系统的学习Jackson的使用方式,感受到了Jackson特别灵活,定制化强的特点,针对于日常的业务需求,已经足够了。更让人惊讶的是自定义的Jackson注解,更是让Jackson如虎添翼。

评论