Jackson使用

由于FastJson存在的不确定性问题,进而选择市面上比较主流,Spring Boot默认绑定的JSON类库:Jackson

本文就来讲解一下Jackson的基本使用以及与Spring Boot的结合与实践。

原文链接:《放弃FastJson!一篇就够,Jackson的功能原来如此之牛(万字干货)

什么是Jackson

Jackson是比较主流的基于Java的JSON类库,可用于JsonXMLJavaBean之间的序列化和反序列化

没看错,Jackson也可以处理JavaBean与XML之间的转换,基于jackson-dataformat-xml组件,而且比较JDK自带XML实现更加高效和安全。而我们使用比较多的是处理JSON与JavaBean之间的功能。

Jackson主流到什么程度?

单从Maven仓库中的统计来看,Jackson的使用量排位第一。

而Spring Boot支持的三个JSON库(Gson、Jackson、JSON-B)中,Jackson是首选默认库。

Jackson也有以下特点:依赖少,简单易用,解析大Json速度快、内存占用比较低、拥有灵活的API、方便扩展与定制。

Jackson类库GitHub地址:https://github.com/FasterXML/jackson

Jackson的组成部分

Jackson的核心模块由三部分组成(从Jackson 2.x开始):jackson-core、jackson-annotations、jackson-databind。

  • jackson-core:核心包,定义了低级流(Streaming)API,提供基于”流模式”解析。Jackson内部实现正是通过高性能的流模式API的JsonGenerator和JsonParser来生成和解析json。
  • jackson-annotations:注解(Annotations)包,提供标准的Jackson注解功能;
  • jackson-databind:数据绑定(Databind)包,实现了数据绑定(和对象序列化)支持,它依赖于Streaming和Annotations包。提供基于“对象绑定”解析的API(ObjectMapper)和”树模型”解析的API(JsonNode);基于”对象绑定”解析的API和”树模型”解析的API依赖基于“流模式”解析的API。

下面看一下不同环境下相关组件的依赖引入情况。

在SpringBoot当中,spring-boot-starter-web 间接引入了Jackson组件,也就是如果你使用了SpringBoot框架,那么你的项目中已经有了Jackson 依赖。下面的依赖省略了version和scope项。

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

web starter中依赖了json starter:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>

json starter最终引入了Jackson:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</dependency>

上面已经提到过,jackson-databind 依赖于 Streaming 和 Annotations 包,因此,引入 jackson-databind 相当于引入了 jackson-core 和 jackson-annotations。

通常情况下,我们单独使用时,根据需要通过 Maven 引入 jackson-databind、jackson-core 和 jackson-annotations 即可。

1
2
3
4
5
6
7
8
9
10
11
12
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

对于SpringBoot项目,基本上不用再额外添加依赖。

Jackson核心类ObjectMapper

Jackson提供了三种JSON的处理方式,分别是:数据绑定、JSON树模型、流式API。

其中前两项功能都是基于ObjectMapper来实现的,而流式API功能则需要基于更底层的JsonGenerator和JsonParser来实现。

通常情况下我们使用ObjectMapper类就足够了,它拥有以下功能:

  • 从字符串、流或文件中解析JSON,并创建表示已解析的JSON的Java对象(反序列化)。
  • 将Java对象构建成JSON字符串(序列化)。
  • 将JSON解析为自定义类的对象,也可以解析JSON树模型的对象。

ObjectMapper基于JsonParser和JsonGenerator来实现JSON实际的读/写。

这一点看一下ObjectMapper的构造方法即可明白。

具体实例

Jackson的常见使用,就不逐一讲解了,通过一些列的实例给大家展示一下,每个实例当中都会通过注释进行说明。

常见简单使用

下面的示例是我们经常会用到的用法演示,主要涉及到JavaBean和Json字符串之间的转换。

Jackson在将json转换为JavaBean属性时,默认是通过Json字段的名称与Java对象中的 getter 和 setter 方法进行匹配进行绑定。

Jackson取getter和setter方法名称中去除“get”和“set”部分,并将首字母小写。例如Json中的name,与JavaBean中的getName()和setName()进行匹配。

但并不是所有的属性都可以被序列化和反序列化,基本上遵循一下规则:

  • public修饰的属性可序列化和反序列化。

  • 属性提供public的getter/setter方法,该属性可序列化和反序列化。

  • 属性只有public的setter方法,而无public的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
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    @Slf4j
    public class JacksonTest {
    /**
    * JavaBean转JSON字符串
    */
    @Test
    public void testJavaBeanToJson() {
    WeChat weChat = new WeChat();
    weChat.setId("zhuan2quan");
    weChat.setName("程序新视界");
    weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"});

    ObjectMapper mapper = new ObjectMapper();
    try {
    String result = mapper.writeValueAsString(weChat);
    System.out.println(result);
    } catch (JsonProcessingException e) {
    log.error("转换异常", e);
    }
    }

    /**
    * JSON字符串转JavaBean
    */
    @Test
    public void testJsonToJavaBean() {
    String json = "{\"id\":\"zhuan2quan\",\"name\":\"程序新视界\",\"interest\":[\"Java\",\"Spring Boot\",\"JVM\"]}";
    ObjectMapper mapper = new ObjectMapper();
    try {
    WeChat weChat = mapper.readValue(json, WeChat.class);
    System.out.println(weChat);
    } catch (JsonProcessingException e) {
    log.error("解析异常", e);
    }
    }

    /**
    * JSON字符串转Map集合
    */
    @Test
    public void testJsonToMap() {
    String json = "{\"id\":\"zhuan2quan\",\"name\":\"程序新视界\",\"interest\":[\"Java\",\"Spring Boot\",\"JVM\"]}";
    ObjectMapper mapper = new ObjectMapper();
    try {
    // 对泛型的反序列化,使用TypeReference可以明确的指定反序列化的类型。
    Map<String, Object> map = mapper.readValue(json, new TypeReference<Map<String, Object>>() {
    });
    System.out.println(map);
    } catch (JsonProcessingException e) {
    log.error("解析异常", e);
    }
    }

    /**
    * JavaBean转文件
    */
    @Test
    public void testJavaBeanToFile() {
    WeChat weChat = new WeChat();
    weChat.setId("zhuan2quan");
    weChat.setName("程序新视界");
    weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"});

    ObjectMapper mapper = new ObjectMapper();
    try {
    //写到文件
    mapper.writeValue(new File("/json.txt"), weChat);

    //从文件中读取
    WeChat weChat1 = mapper.readValue(new File("/json.txt"), WeChat.class);
    System.out.println(weChat1);
    } catch (IOException e) {
    log.error("转换异常", e);
    }
    }

    /**
    * JavaBean转字节流
    */
    @Test
    public void testJavaBeanToBytes() {
    WeChat weChat = new WeChat();
    weChat.setId("zhuan2quan");
    weChat.setName("程序新视界");
    weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"});

    ObjectMapper mapper = new ObjectMapper();
    try {
    // 写为字节流
    byte[] bytes = mapper.writeValueAsBytes(weChat);
    // 从字节流读取
    WeChat weChat1 = mapper.readValue(bytes, WeChat.class);
    System.out.println(weChat1);
    } catch (IOException e) {
    log.error("转换异常", e);
    }
    }
    }

上述代码用到了lombok注解和单元测试注解,根据需要可进行替换。

JSON树模型

如果Json字符串比较大,则可使用JSON树模型来灵活的获取所需的字段内容。在Jackson中提供了get、path、has等方法来获取或判断。

下面直接看两个示例:

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
@Slf4j
public class JacksonNodeTest {

/**
* JavaBean转JSON字符串
*/
@Test
public void testJsonNode() {
// 构建JSON树
ObjectMapper mapper = new ObjectMapper();
ObjectNode root = mapper.createObjectNode();
root.put("id", "zhuan2quan");
root.put("name", "程序新视界");
ArrayNode interest = root.putArray("interest");
interest.add("Java");
interest.add("Spring Boot");
interest.add("JVM");

// JSON树转JSON字符串
String json = null;
try {
json = mapper.writeValueAsString(root);
} catch (JsonProcessingException e) {
log.error("Json Node转换异常", e);
}
System.out.println(json);
}

/**
* 解析JSON字符串为JSON树模型
*/
@Test
public void testJsonToJsonNode() {
String json = "{\"id\":\"zhuan2quan\",\"name\":\"程序新视界\",\"interest\":[\"Java\",\"Spring Boot\",\"JVM\"]}";
ObjectMapper mapper = new ObjectMapper();
try {
// 将JSON字符串转为JSON树
JsonNode jsonNode = mapper.readTree(json);
String name = jsonNode.path("name").asText();
System.out.println(name);

JsonNode interestNode = jsonNode.get("interest");
if (interestNode.isArray()){
for (JsonNode node : interestNode){
System.out.println(node.asText());
}
}
} catch (JsonProcessingException e) {
log.error("Json Node转换异常", e);
}
}
}

其中get方法和path功能相似,区别在于如果要读取的key在Json串中不存在时,get方法会null,而path会返回MissingNode实例对象,在链路方法情况下保证不会抛出异常。

流式API

除了上述两种形式,还可以基于底层的流式API来进行操作,主要通过JsonGenerator和JsonParser两个API,但操作起来比较复杂,就不再这里演示了。

格式化统一配置

在使用ObjectMapper时,会存在一些字段在某些情况下不需要进行序列化或反序列化,同时还可能需要指定格式化的一些信息等。此时,可以通过ObjectMapper进行配置。

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

针对于配置项,在2.2版本中新增了一个ObjectMapper的实现类JsonMapper,功能与ObjectMapper一致。

不过新增了一个builder方法。可以直接通过JsonMapper.builder().configure()方法来进行配置,最后获得一个JsonMapper对象。

JsonMapper的其他方法基本都集成自ObjectMapper。

注解的使用

上面通过统一配置可对全局格式的序列化和反序列化进行配置,但某些个别的场景下,需要针对具体的字段进行配置,这就需要用注解。

比如当Json字符串中的字段与Java对象中的属性不一致时,就需要通过注解来建立它们直接的关系。

@JsonProperty,作用JavaBean字段上,指定一个字段用于JSON映射,默认情况下映射的JSON字段与注解的字段名称相同。可通过value属性指定映射的JSON的字段名称。

@JsonIgnore可用于字段、getter/setter、构造函数参数上,指定字段不参与序列化和反序列化。

@JsonIgnoreProperties作用于类上,序列化时@JsonIgnoreProperties({“prop1”, “prop2”})会忽略pro1和pro2两个属性。

反序列化时@JsonIgnoreProperties(ignoreUnknown=true)会忽略类中不存在的字段。

@JsonFormat作用于字段上,通常用来进行格式化操作。

1
2
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date date;

如果JavaBean中的时间字段使用的是JDK8新增的时间日期(LocalDate/LocalTime/LocalDateTime)类型的话,需要添加jackson-datatype-jsr310依赖。在讲依赖部分时,SpringBoot默认引入的依赖中就有这个。

当然,还有一些其他的注解,比如@JsonPropertyOrder、@JsonRootName、@JsonAnySetter、@JsonAnyGetter、@JsonNaming等,当使用时参考对应的文档和示例看一下就可以,这里就不再一一列出了。

自定义解析器

如果上面的注解和统一配置还无法满足需求,可自定义解析器,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class MyFastjsonDeserialize extends JsonDeserializer<Point> {

@Override
public Point deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
Iterator<JsonNode> iterator = node.get("coordinates").elements();
List<Double> list = new ArrayList<>();
while (iterator.hasNext()) {
list.add(iterator.next().asDouble());
}
return new Point(list.get(0), list.get(1));
}
}

定义完成之后,注册到Mapper中:

1
2
3
4
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Point.class, new MyFastjsonDeserialize());
objectMapper.registerModule(module);

Jackson处理XML

Jackson也可以通过jackson-dataformat-xml包提供了处理XML的功能。在处理XML时建议使用woodstox-core包,它是一个XML的实现,比JDK自带XML实现更加高效,也更加安全。

1
2
3
4
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

如果使用Java 9及以上版本,可能会出现java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException异常,这是因为Java 9实现了JDK的模块化,将原本和JDK打包在一起的JAXB实现分隔出来。所以需要手动添加JAXB的实现。

1
2
3
4
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>

下面是代码示例,基本上和JSON的API非常相似,XmlMapper实际上就是ObjectMapper的子类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Test
public void testXml(){
WeChat weChat = new WeChat();
weChat.setId("zhuan2quan");
weChat.setName("程序新视界");
weChat.setInterest(new String[]{"Java", "Spring Boot", "JVM"});

XmlMapper xmlMapper = new XmlMapper();
try {
String xml = xmlMapper.writeValueAsString(weChat);
System.out.println(xml);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}

执行之后,输出结果:

1
2
3
4
5
6
7
8
9
<WeChat>
<id>zhuan2quan</id>
<name>程序新视界</name>
<interest>
<interest>Java</interest>
<interest>Spring Boot</interest>
<interest>JVM</interest>
</interest>
</WeChat>

Spring Boot中的集成

在最开始的时候,我们已经看到Spring Boot默认引入了Jackson的依赖,而且也用我们做什么额外的操作,其实已经在使用Jackson进行Json格式的数据与MVC中参数进行绑定操作了。

如果Spring Boot默认的配置并不适合项目需求,也可以通过内置的配置进行配置,以application.yml配置为例,可通过指定以下属性进行相应选项的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 指定日期格式,比如yyyy-MM-dd HH:mm:ss,或者具体的格式化类的全限定名
spring.jackson.date-format
# 是否开启Jackson的反序列化
spring.jackson.deserialization
# 是否开启json的generators.
spring.jackson.generator
# 指定Joda date/time的格式,比如yyyy-MM-ddHH:mm:ss). 如果没有配置的话,dateformat会作为backup
spring.jackson.joda-date-time-format
# 指定json使用的Locale.
spring.jackson.locale
# 是否开启Jackson通用的特性.
spring.jackson.mapper
# 是否开启jackson的parser特性.
spring.jackson.parser
# 指定PropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)或者指定PropertyNamingStrategy子类的全限定类名.
spring.jackson.property-naming-strategy
# 是否开启jackson的序列化.
spring.jackson.serialization
# 指定序列化时属性的inclusion方式,具体查看JsonInclude.Include枚举.
spring.jackson.serialization-inclusion
# 指定日期格式化时区,比如America/Los_Angeles或者GMT+10.
spring.jackson.time-zone

Spring Boot自动配置非常方便,但某些时候需要我们手动配置Bean来替代自动配置的Bean。可通过如下形式进行配置:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class JacksonConfig {
@Bean
@Qualifier("json")
public ObjectMapper jsonMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper mapper = builder.createXmlMapper(false)
.build();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}

配置完成,可在使用的地方直接注入即可:

1
2
@Resource
private ObjectMapper jsonMapper;

对于上面的注入,可能会有朋友问了,是否有线程安全的问题?这个不用担心ObjectMapper是线程安全的。

小结

经过本篇文章的讲解,大家对Jackson应该有一个比较全面的了解了。

就个人而言,学习Jackson之后,感觉还是挺有意思的。

使用篇

文章转自: https://blog.csdn.net/qidasheng2012/article/details/105771052

简介

日常开发过程中经常会使用json进行数据的传输,这就涉及到了对象和json的相互转化,常用的解决方案有:

  • Jackson(推荐)
  • 谷歌的Gson
  • 阿里的Fastjson

详情见 Json【汇总】

因为SpringBoot默认的json解析格式就是Jackson,所以不需要额外的引入依赖。

常见配置

参考自:SpringBoot Jackson配置项说明及其自定义序列化器

方式一:yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 指定日期格式,比如yyyy-MM-dd HH:mm:ss,或者具体的格式化类的全限定名
spring.jackson.date-format
# 指定日期格式化时区,比如America/Los_Angeles或者GMT+10.
spring.jackson.time-zone
# 是否开启Jackson的反序列化
spring.jackson.deserialization
# 是否开启json的generators.
spring.jackson.generator
# 指定Joda date/time的格式,比如yyyy-MM-ddHH:mm:ss). 如果没有配置的话,dateformat会作为backup
spring.jackson.joda-date-time-format
# 指定json使用的Locale.
spring.jackson.locale
# 是否开启Jackson通用的特性.
spring.jackson.mapper
# 是否开启jackson的parser特性.
spring.jackson.parser
# 指定PropertyNamingStrategy(CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)或者指定PropertyNamingStrategy子类的全限定类名.
spring.jackson.property-naming-strategy
# 是否开启jackson的序列化.
spring.jackson.serialization
# 指定序列化时属性的inclusion方式,具体查看JsonInclude.Include枚举.
spring.jackson.serialization-inclusion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spring:
jackson:
# 日期格式化
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
# 设置空如何序列化
default-property-inclusion: non_null
serialization:
# 格式化输出
indent_output: true
# 忽略无法转换的对象
fail_on_empty_beans: false
deserialization:
# 允许对象忽略json中不存在的属性
fail_on_unknown_properties: false
parser:
# 允许出现特殊字符和转义符
allow_unquoted_control_chars: true
# 允许出现单引号
allow_single_quotes: true

方式二:重新注入ObjectMapper

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
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder{
ObjectMapper objectMapper = builder.createXmlMapper(false).build();

// 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化
// Include.Include.ALWAYS 默认
// Include.NON_DEFAULT 属性为默认值不序列化
// Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
// Include.NON_NULL 属性为NULL 不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 允许出现特殊字符和转义符
objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
// 允许出现单引号
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 字段保留,将null值转为""
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>()
{
@Override
public void serialize(Object o, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider)
throws IOException
{
jsonGenerator.writeString("");
}
});
return objectMapper;
}

常用 API

因为日常开发最常用的api就是对象的序列化和反序列化,这里封装一个工具类,这里只展示部分API,其他API可参考 JSON解析-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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package com.it.jackson.util;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class JsonUtils {

private static ObjectMapper mapper = new ObjectMapper();

static {
// 对于空的对象转json的时候不抛出错误
mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
// 允许属性名称没有引号
mapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 允许单引号
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 设置输入时忽略在json字符串中存在但在java对象实际没有的属性
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 设置输出时包含属性的风格
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}


/**
* 序列化,将对象转化为json字符串
*
* @param data
* @return
*/
public static String toJsonString(Object data) {
if (data == null) {
return null;
}

String json = null;
try {
json = mapper.writeValueAsString(data);
} catch (JsonProcessingException e) {
log.error("[{}] toJsonString error:{{}}", data.getClass().getSimpleName(), e);
}
return json;
}


/**
* 反序列化,将json字符串转化为对象
*
* @param json
* @param clazz
* @param <T>
* @return
*/
public static <T> T parse(@NonNull String json, Class<T> clazz) {
T t = null;
try {
t = mapper.readValue(json, clazz);
} catch (Exception e) {
log.error(" parse json [{}] to class [{}] error:{{}}", json, clazz.getSimpleName(), e);
}
return t;
}

}

定义一个Bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.it.jackson.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private Date birthday;
}

测试:

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
package com.it.jackson.test;

import com.it.jackson.domain.User;
import com.it.jackson.util.JsonUtils;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;

@Slf4j
public class JacksonTest {
public static void main(String[] args) {
User user = User.builder()
.id(1L)
.name("tom")
.age(23)
.birthday(new Date())
.build();

String json = JsonUtils.toJsonString(user);
log.info("obj toJsonString:[{}]", json);

User u = JsonUtils.parse(json, User.class);
log.info("parse json to obj: [{}]", u);

}
}

控制台日志:

1
2
16:44:01.871 [main] INFO com.it.jackson.test.JacksonTest - obj toJsonString:[{"id":1,"name":"tom","age":23,"birthday":1587890641047}]
16:44:01.941 [main] INFO com.it.jackson.test.JacksonTest - parse json to obj: [User(id=1, name=tom, age=23, birthday=Sun Apr 26 16:44:01 CST 2020)]

常用 注解

参考自:Jackson使用手册

【@JsonProperty】类似于sql里字段的别名,用于序列化,使用注解字段属性,替代原字段属性

1
2
@JsonProperty("userName")
private String name;

序列化结果为:在序列化的json串中,userName替代了name

1
{"userName":"tom"}

【@JsonIgnore】在序列化时忽略该字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
@JsonIgnore
private Long id;
@JsonProperty("userName")
private String name;
@JsonIgnore
private Integer age;
@JsonIgnore
private Date birthday;
}

序列化结果为:

1
{"userName":"tom"}

【@JsonIgnoreProperties】

1、序列化@JsonIgnoreProperties与@JsonIgnore类似,用于类上,注解使用的是字段别名

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

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonIgnoreProperties({"id","userName","birthday"})
public class User {
private Long id;
@JsonProperty("userName")
private String name;
private Integer age;
private Date birthday;
}

序列化结果为:

1
{"age":23}

2、@JsonIgnoreProperties(ignoreUnknown = true)用于忽略字段不匹配情况,相当于mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);

【@JsonTypeName @JsonTypeInfo】用在类上,在序列化时增加一层

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
package com.it.jackson.domain;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@JsonTypeName(value = "user")
@JsonTypeInfo(include = JsonTypeInfo.As.WRAPPER_OBJECT, use = JsonTypeInfo.Id.NAME)
public class User {
private Long id;
@JsonProperty("userName")
private String name;
private Integer age;
private Date birthday;
}

序列化结果:

1
{"user":{"id":1,"age":23,"birthday":1587891781603,"userName":"tom"}}

【@JsonRootName】

组合在序列化上等于类上注解@JsonRootName(“user”) 和 mapper.enable(SerializationFeature.WRAP_ROOT_VALUE),反序列化无用;

【@JsonForma】格式化日期格式

1
2
3
4
5
6
7
8
9
10
11
12
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
@JsonProperty("userName")
private String name;
private Integer age;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss:SSS", timezone = "GMT+8")
private Date birthday;
}

序列化结果:

1
{"id":1,"age":23,"birthday":"2020-04-26 17:09:32:818","userName":"tom"}

本文标题:Jackson使用

文章作者:LiJing

发布时间:2021年12月29日 - 11:36:22

最后更新:2023年06月03日 - 10:01:35

原始链接:https://blog-next.xiaojingge.com/posts/2165476725.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------------本文结束 感谢您的阅读-------------------