Java JSON处理
# 前言
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它以易于阅读和编写的文本格式表示结构化数据。
JSON最初由Douglas Crockford
在2001年提出,并且广泛应用于Web应用程序之间的数据交换。
# JSON的核心特点
- 简洁性:JSON使用简单的文本格式,易于阅读和编写
- 可读性:JSON的结构对开发人员来说非常直观和易于理解
- 跨平台支持:作为通用的数据交换格式,可以在不同平台和编程语言之间进行交换和解析
- 语言无关性:JSON与特定的编程语言无关,可以在多种编程语言中使用
- 扩展性:支持嵌套和复杂的数据结构,可以表示大规模的数据
- Web友好:与JavaScript密切相关,在Web应用程序中广泛使用,特别是RESTful API
# JSON的数据类型
JSON支持以下基本数据类型:
- 字符串(String):用双引号包围的文本
- 数字(Number):整数或浮点数
- 布尔值(Boolean):
true
或false
- 数组(Array):有序的值列表,用方括号
[]
表示 - 对象(Object):键值对集合,用花括号
{}
表示 - 空值(Null):
null
# JSON示例
下面是一个完整的JSON对象示例,展示了各种数据类型的使用:
{
"name": "John", // 字符串类型
"age": 30, // 数字类型
"city": "New York", // 字符串类型
"hobbies": [ // 数组类型
"reading",
"coding",
"traveling"
],
"isStudent": false, // 布尔类型
"address": { // 嵌套对象
"street": "123 Main St",
"zip": "10001"
},
"spouse": null // 空值类型
}
# Java中的JSON应用场景
在Java开发中,JSON的使用场景非常广泛,主要包括:
# 1. 数据交换
JSON作为通用的数据交换格式,可以在不同系统、不同编程语言之间进行数据交换。在Java中,可以使用JSON来序列化Java对象为JSON格式,并将其发送给其他系统或服务。
# 2. Web服务与RESTful API
- Spring MVC:默认使用Jackson进行JSON处理
- JAX-RS:支持多种JSON处理库
- Spring Boot:自动配置JSON序列化/反序列化
# 3. 数据存储
- NoSQL数据库:如MongoDB、Elasticsearch等原生支持JSON
- 关系型数据库:PostgreSQL、MySQL 5.7+支持JSON数据类型
- 文件存储:将配置或数据以JSON格式保存
# 4. 微服务通信
- 服务间调用:使用JSON作为消息格式
- 消息队列:RabbitMQ、Kafka等消息中间件的消息格式
- 服务注册与发现:配置信息的传递
# 5. 配置管理
- 应用配置:Spring Boot的
application.json
- 动态配置:配置中心(如Apollo、Nacos)使用JSON格式
# 6. 测试数据
- Mock数据:模拟外部服务响应
- 测试用例:定义测试输入和期望输出
- 接口文档:Swagger/OpenAPI规范使用JSON描述API
# Java中的JSON库概述
在Java生态系统中,有多个成熟的JSON处理库可供选择:
# 主流JSON库对比
特性 | Jackson | Gson | Fastjson2 |
---|---|---|---|
性能 | 优秀 | 良好 | 最快 |
功能丰富度 | 最全面 | 适中 | 丰富 |
社区支持 | 最活跃 | 活跃 | 活跃 |
Spring集成 | 默认支持 | 需配置 | 需配置 |
安全性 | 优秀 | 优秀 | 已改进 |
学习曲线 | 适中 | 简单 | 简单 |
# Jackson的核心优势
Spring框架默认选择Jackson作为JSON处理库,主要基于以下优势:
卓越的性能
- 高效的流式API(Streaming API)
- 优化的内存使用
- 快速的序列化/反序列化速度
强大的功能特性
- 支持复杂的类型处理(泛型、多态)
- 灵活的自定义序列化器/反序列化器
- 完善的注解体系
多格式支持
- JSON(默认)
- XML(jackson-dataformat-xml)
- YAML(jackson-dataformat-yaml)
- CSV(jackson-dataformat-csv)
- Properties(jackson-dataformat-properties)
- CBOR、Smile等二进制格式
模块化架构
jackson-core
:核心流处理jackson-databind
:数据绑定jackson-annotations
:注解支持- 丰富的扩展模块
企业级特性
- 线程安全的
ObjectMapper
- 完善的异常处理
- 详细的文档和社区支持
- 线程安全的
本篇文章主要介绍Jackson的使用,同时简要介绍其他主流库。
# 其他主流JSON库简介
# Gson
Gson是Google开发的JSON处理库,以简单易用著称:
- 优点:API简洁、学习成本低、适合简单场景
- 缺点:性能相对较弱、功能不如Jackson丰富
- 使用场景:Android开发、简单的JSON处理需求
- 参考文档:Gson用户指南 (opens new window)
# Fastjson2
Fastjson2是阿里巴巴开源的高性能JSON库:
- 优点:极致的性能优化、API简单、中文文档丰富
- 改进:相比Fastjson 1.x,安全性大幅提升
- 使用场景:高性能要求场景、Dubbo 3.2+默认使用
- 性能对比:Fastjson2 Benchmark (opens new window)
- 参考文档:Fastjson2官方文档 (opens new window)
# Jackson的使用
# 1. 添加Jackson依赖
首先,我们需要将Jackson库添加到项目的依赖中。可以在项目的构建文件中添加以下依赖:
<!-- Maven 依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
# 2. 创建和配置ObjectMapper
ObjectMapper
是Jackson的核心类,负责JSON的序列化和反序列化操作。
# 基础创建
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objectMapper = new ObjectMapper();
# 最佳实践:单例模式
ObjectMapper
是线程安全的重量级对象,建议使用单例模式:
public class JsonUtils {
private static final ObjectMapper MAPPER = new ObjectMapper();
static {
// 配置ObjectMapper
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
// 注册模块
MAPPER.registerModule(new JavaTimeModule());
}
public static ObjectMapper getMapper() {
return MAPPER;
}
// 工具方法
public static String toJson(Object obj) throws JsonProcessingException {
return MAPPER.writeValueAsString(obj);
}
public static <T> T fromJson(String json, Class<T> clazz) throws JsonProcessingException {
return MAPPER.readValue(json, clazz);
}
}
⚠️ 注意事项:
ObjectMapper
创建成本高,应避免频繁创建- 线程安全,可在多线程环境共享
- 配置后不应再修改,保证线程安全
# 3. JSON与Java对象的相互转换
Jackson提供了将JSON数据与Java对象相互转换的功能。
# 示例类定义
为了演示Jackson的使用,我们定义以下测试类:
// 使用Java 14+的record(推荐)
record MyClass(String name, Integer age) {}
// 或使用传统的POJO类
public class MyClass {
private String name;
private Integer age;
// 必须有无参构造函数(用于反序列化)
public MyClass() {}
public MyClass(String name, Integer age) {
this.name = name;
this.age = age;
}
// Getters and Setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
}
- JSON字符串转Java对象
要将JSON字符串转换为Java对象,可以使用ObjectMapper的readValue()
方法。以下是一个示例:
String jsonString = "{\"name\":\"John\",\"age\":30}";
// JSON字符串转Java对象
MyClass myObject = objectMapper.readValue(jsonString, MyClass.class);
在上面的示例中,我们将JSON字符串转换为MyClass对象。
- JSON文件转Java对象
要将JSON文件转换为Java对象,可以使用ObjectMapper的readValue()
方法,并将文件作为输入源。以下是一个示例:
File jsonFile = new File("data.json");
// JSON文件转Java对象
MyClass myObject = objectMapper.readValue(jsonFile, MyClass.class);
在上面的示例中,我们将JSON文件转换为MyClass对象。
- Java对象转JSON字符串
要将Java对象转换为JSON字符串,可以使用ObjectMapper的writeValueAsString()
方法。以下是一个示例:
MyClass myObject = new MyClass("John", 30);
// Java对象转JSON字符串
String jsonString = objectMapper.writeValueAsString(myObject);
在上面的示例中,我们将MyClass对象转换为JSON字符串。
- Java对象输出到JSON文件
要将Java对象输出到JSON文件,可以使用ObjectMapper的writeValue()
方法,并将文件作为输出目标。以下是一个示例:
MyClass myObject = new MyClass("John", 30);
File jsonFile = new File("output.json");
// Java对象输出到JSON文件
objectMapper.writeValue(jsonFile, myObject);
在上面的示例中,我们将MyClass对象输出到名为"output.json"的文件中。
# 4. JSON与Java集合的相互转换
除了转换单个Java对象,Jackson还提供了将JSON与Java集合相互转换的功能。
- JSON字符串转Java集合
要将JSON字符串转换为Java集合,可以使用ObjectMapper的readValue()
方法,并指定目标集合类型。以下是一个示例:
String jsonArray = "[{\"name\":\"John\",\"age\":30},{\"name\":\"Alice\",\"age\":25}]";
// JSON字符串转Java集合
MyClass[] myObjects = objectMapper.readValue(jsonArray, MyClass[].class);
// 或者
List<MyClass> myObjects = objectMapper.readValue(jsonArray, new TypeReference<List<MyClass>>() {});
在上面的示例中,我们将JSON数组转换为List<MyClass>
集合。
上面代码中我们看到了TypeReference
,它是Jackson库中的一个类,用于解决Java泛型类型在序列化和反序列化过程中的类型擦除问题。由于Java的泛型信息在运行时被擦除,无法直接获得泛型类型,因此Jackson提供了TypeReference
类来捕获泛型类型的信息。
在使用Jackson进行反序列化时,如果目标类型是一个泛型类型,可以使用TypeReference
来指定泛型类型,并获取准确的类型信息,以便正确地进行反序列化。
通过使用TypeReference
,我们能够解决Java泛型类型的类型擦除问题,确保在反序列化时获得准确的类型信息,从而正确地转换为目标类型。
你也可以把JSON对象转换为Map对象,例如:
List<Map<String,Object> myObjects = objectMapper.readValue(jsonArray, new TypeReference<List<Map<String,Object>>() {});
- JSON文件转Java集合
要将JSON文件转换为Java集合,可以使用ObjectMapper的readValue()
方法,并将文件作为输入源。以下是一个示例:
File jsonFile = new File("data.json");
// JSON文件转Java集合
List<MyClass> myObjects = objectMapper.readValue(jsonFile, new TypeReference<List<MyClass>>() {});
在上面的示例中,我们将JSON文件转换为List<MyClass>
集合。
- Java集合转JSON字符串
要将Java集合转换为JSON字符串,可以使用ObjectMapper的writeValueAsString()
方法。 以下是一个示例:
List<MyClass> myObjects = new ArrayList<>();
myObjects.add(new MyClass("John", 30));
myObjects.add(new MyClass("Alice", 25));
// Java集合转JSON字符串
String jsonArray = objectMapper.writeValueAsString(myObjects);
在上面的示例中,我们将List<MyClass>
集合转换为JSON字符串。
# 5. 复杂对象的处理
演示一下复杂JSON数据的处理,包括嵌套对象、数组等。
假设有如下类:
public class CompositeClass {
private String name;
private int age;
private Address address;
private List<Pet> pets;
// 构造函数、Getter和Setter方法等
public static class Address {
private String street;
private String city;
// 构造函数、Getter和Setter方法等
// 省略其他代码
}
public static class Pet {
private String name;
private String species;
// 构造函数、Getter和Setter方法等
// 省略其他代码
}
// 省略其他代码
}
要把JSON转换为CompositeClass对象,可以使用ObjectMapper的readValue()
方法,并指定目标类型。以下是一个示例:
String jsonString = "{\"name\":\"John\",\"age\":30,\"address\":{\"street\":\"123 Main St\",\"city\":\"New York\"},\"pets\":[{\"name\":\"Fluffy\",\"species\":\"cat\"},{\"name\":\"Buddy\",\"species\":\"dog\"}]}";
// 将JSON转换为Java对象
MyClass myObject = objectMapper.readValue(jsonString, MyClass.class);
// 访问Java对象中的数据
String name = myObject.getName();
int age = myObject.getAge();
String street = myObject.getAddress().getStreet();
String city = myObject.getAddress().getCity();
List<Pet> pets = myObject.getPets();
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Street: " + street);
System.out.println("City: " + city);
System.out.println("Pets:");
for (Pet pet : pets) {
String petName = pet.getName();
String species = pet.getSpecies();
System.out.println(" Pet Name: " + petName);
System.out.println(" Species: " + species);
}
# 6. JsonNode的用法
ObjectMapper的readTree()
方法接受一个JSON字符串作为参数,并返回一个JsonNode对象,表示解析后的JSON数据的树形结构。
以下是一个示例:
String jsonString = "{\"name\":\"John\",\"age\":30,\"address\":{\"street\":\"123 Main St\",\"city\":\"New York\"},\"pets\":[{\"name\":\"Fluffy\",\"species\":\"cat\"},{\"name\":\"Buddy\",\"species\":\"dog\"}]}";
// 将JSON字符串转换为JsonNode对象
JsonNode jsonNode = objectMapper.readTree(jsonString);
// 访问JsonNode中的数据
String name = jsonNode.get("name").asText();
int age = jsonNode.get("age").asInt();
String street = jsonNode.get("address").get("street").asText();
String city = jsonNode.get("address").get("city").asText();
JsonNode pets = jsonNode.get("pets");
System.out.println("Name: " + name);
System.out.println("Age: " + age);
System.out.println("Street: " + street);
System.out.println("City: " + city);
System.out.println("Pets:");
for (JsonNode pet : pets) {
String petName = pet.get("name").asText();
String species = pet.get("species").asText();
System.out.println(" Pet Name: " + petName);
System.out.println(" Species: " + species);
}
# 7. 美化JSON输出
如果需要对生成的JSON进行格式化和美化,可以使用ObjectMapper的writerWithDefaultPrettyPrinter()
方法。以下是一个示例:
MyClass myObject = new MyClass("John", 30);
// Java对象转JSON字符串(格式化输出)
String jsonString = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(myObject);
# 8. JSON处理的行为和特性配置
ObjectMapper.configure()
是Jackson库中ObjectMapper
类的一个方法,用于配置ObjectMapper
的各种行为和特性。通过该方法,可以自定义和控制ObjectMapper
在序列化和反序列化过程中的行为。
configure()
方法接受两个参数:一个枚举类型的配置选项,和一个布尔值参数,用于指定配置选项的设置值。下面是一些常用的配置选项及其说明:
DeserializationFeature
:用于配置反序列化时的行为选项,例如处理未知属性、忽略空值、支持使用单引号等。FAIL_ON_UNKNOWN_PROPERTIES
: 控制是否在遇到未知属性时抛出异常。设置为false
表示忽略未知属性,默认为true
。ACCEPT_EMPTY_STRING_AS_NULL_OBJECT
: 指定是否将空字符串解析为null
对象。默认为false
。UNWRAP_SINGLE_VALUE_ARRAYS
: 控制是否自动将包含单个值的数组解包。默认为false
。USE_BIG_DECIMAL_FOR_FLOATS
: 控制是否使用BigDecimal
来表示浮点数。默认为false
,使用double
表示浮点数。USE_BIG_INTEGER_FOR_INTS
: 控制是否使用BigInteger
来表示整数。默认为false
,使用int
或long
表示整数。
SerializationFeature
:用于配置序列化时的行为选项,例如处理空值、缩进输出、使用字面值输出枚举等。INDENT_OUTPUT
: 控制是否缩进输出的JSON。默认为false
。WRITE_DATES_AS_TIMESTAMPS
: 控制是否将日期写入为时间戳。默认为true
。WRITE_NULL_MAP_VALUES
: 控制是否写入空值字段。默认为true
。WRITE_ENUMS_USING_TO_STRING
: 控制是否使用toString()
方法来序列化枚举值。默认为false
,使用枚举常量名称。WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS
: 控制是否将字符数组写入为JSON数组。默认为false
。
JsonParser.Feature
:用于配置JSON解析器的行为选项,例如支持单引号、允许注释等。ALLOW_COMMENTS
: 控制是否允许在JSON中包含注释。默认为false
。ALLOW_SINGLE_QUOTES
: 控制是否允许使用单引号作为字符串的引号。默认为false
。ALLOW_UNQUOTED_FIELD_NAMES
: 控制是否允许在JSON中使用非引号括起的字段名称。默认为false
。
JsonGenerator.Feature
:用于配置JSON生成器的行为选项,例如缩进输出、使用双引号包装属性等。QUOTE_FIELD_NAMES
: 控制是否在生成的JSON中将字段名称用引号括起。默认为true
。WRITE_BIGDECIMAL_AS_PLAIN
: 控制是否将BigDecimal
写入为普通数字,而不是科学计数法。默认为false
。
通过调用ObjectMapper.configure()
方法,可以根据具体需求设置上述选项及其他选项。以下是一个示例代码,展示了如何使用configure()
方法设置ObjectMapper
的配置选项:
ObjectMapper OBJECT_MAPPER = new ObjectMapper();
// 反序列化时,当遇到未知的属性(在JSON中存在但在目标Java类中不存在)时,控制是否抛出异常。通过将其设置为false,可以忽略未知的属性而不抛出异常。
OBJECT_MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 序列化时,控制是否将字符数组写入为JSON数组。默认情况下,字符数组被序列化为字符串,但将其设置为true可以将字符数组序列化为JSON数组。
OBJECT_MAPPER.configure(SerializationFeature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS, true);
// 解析时,控制是否允许在JSON中使用单引号作为字符串的引号。将其设置为true可以允许使用单引号来表示字符串,而不仅限于双引号。
OBJECT_MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
# 9. 时间处理
Jackson默认将Date
序列化为时间戳格式(从1970年1月1日UTC开始的毫秒数)。
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = df.parse("01-01-1970 01:00");
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
mapper.writeValueAsString(event);
实际输出的结果如下:
{
"name": "party",
"eventDate": 3600000
}
# 配置ObjectMapper日期格式
配置ObjectMapper
以允许我们设置日期的格式:
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
String toParse = "20-12-2014 02:30";
Date date = df.parse(toParse);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(df);
String result = mapper.writeValueAsString(event);
// result = toParse
请注意,我们使用了全局配置,影响整个ObjectMapper
的行为。
# 使用@JsonFormat格式化日期
可以使用@JsonFormat
注解在单个类上控制日期格式,而不是全局配置整个应用程序的日期格式:
public class Event {
public String name;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
public Date eventDate;
}
现在让我们进行测试:
@Test
public void whenUsingJsonFormatAnnotationToFormatDate_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
df.setTimeZone(TimeZone.getTimeZone("UTC"));
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(event);
assertThat(result, containsString(toParse));
}
# 自定义日期序列化器
为了完全控制输出结果,我们可以使用自定义的日期序列化器:
public class CustomDateSerializer extends StdSerializer<Date> {
private SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateSerializer() {
this(null);
}
public CustomDateSerializer(Class<Date> t) {
super(t);
}
@Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
gen.writeString(formatter.format(value));
}
}
现在,我们将它作为"eventDate"字段的序列化器:
public class Event {
public String name;
@JsonSerialize(using = CustomDateSerializer.class)
public Date eventDate;
}
最后,让我们进行测试:
@Test
public void whenUsingCustomDateSerializer_thenCorrect()
throws JsonProcessingException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
String toParse = "20-12-2014 02:30:00";
Date date = df.parse(toParse);
Event event = new Event("party", date);
ObjectMapper mapper = new ObjectMapper();
String result = mapper.writeValueAsString(event);
assertThat(result, containsString(toParse));
}
# 使用Jackson序列化Joda-Time
日期并不总是java.util.Date
的实例。实际上,越来越多的情况下,日期是由其他类表示的,而常见的一种是Joda-Time库中的DateTime
类。
让我们看看如何使用Jackson对DateTime
进行序列化。
我们将使用jackson-datatype-joda
模块来支持Joda-Time的序列化:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>
<version>2.15.2</version>
</dependency>
然后,我们只需要注册JodaModule
即可:
@Test
public void whenSerializingJodaTime_thenCorrect()
throws JsonProcessingException {
DateTime date = new DateTime(2014, 12, 20, 2, 30,
DateTimeZone.forID("Europe/London"));
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String result = mapper.writeValueAsString(date);
assertThat(result, containsString("2014-12-20T02:30:00.000Z"));
}
# 使用Jackson序列化Java 8日期
现在让我们看看如何使用Jackson对Java 8日期(例如LocalDateTime
)进行序列化。我们可以使用jackson-datatype-jsr310
模块:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.15.2</version>
</dependency>
然后,我们只需要注册JavaTimeModule
(JSR310Module
已被弃用),Jackson将会处理剩下的事情:
@Test
public void whenSerializingJava8Date_thenCorrect()
throws JsonProcessingException {
LocalDateTime date = LocalDateTime.of(2014, 12, 20, 2, 30);
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String result = mapper.writeValueAsString(date);
assertThat(result, containsString("2014-12-20T02:30"));
}
# 10. 自定义序列化和反序列化
在时间处理一章中,我们已经做到了自定义序列化处理,现在补充一下自定义Date的反序列化处理:
public class CustomDateDeserializer extends StdDeserializer<Date> {
private SimpleDateFormat formatter =
new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Date deserialize(JsonParser jsonparser, DeserializationContext context)
throws IOException, JsonProcessingException {
String date = jsonparser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
接下来,我们将将其用作"eventDate"的反序列化器:
public class Event {
public String name;
@JsonDeserialize(using = CustomDateDeserializer.class)
public Date eventDate;
}
最后,我们将进行测试:
@Test
public void whenDeserializingDateUsingCustomDeserializer_thenCorrect()
throws JsonProcessingException, IOException {
String json = "{\"name\":\"party\",\"eventDate\":\"20-12-2014 02:30:00\"}";
SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
ObjectMapper mapper = new ObjectMapper();
Event event = mapper.readerFor(Event.class).readValue(json);
assertEquals("20-12-2014 02:30:00", df.format(event.eventDate));
}
# Jackson的datatype模块
在前面我们已经看到了Jackson本身提供了很多datatype模块,用于提供对不同数据类型的序列化和反序列化支持。它包含了一些特定类型的序列化和反序列化器,使得在处理特定数据类型时更加方便和灵活。
除了日期和时间类型,datatype模块还提供了对其他数据类型的支持,如Guava类型、Java8类型、PCollections类型等。通过使用相应的模块,可以轻松地处理这些特定类型的序列化和反序列化。
要在项目中使用datatype模块,需要将相应的依赖项添加到项目的构建文件(如pom.xml)。具体依赖项的名称和版本号可以根据需要和使用的Jackson版本进行调整。
以下是常用的datatype模块依赖项:
jackson-datatype-jsr310
:提供对Java 8日期和时间API的支持。jackson-module-parameter-names
:提供了对使用新的JDK 8特性的支持的模块,能够访问构造函数和方法参数的名称,以允许省略@JsonProperty。jackson-datatype-jdk8
:支持除日期/时间类型以外的JDK 8数据类型,包括Optional。jackson-datatype-joda
:提供对Joda-Time库的支持。jackson-datatype-guava
:提供对Guava库的支持。jackson-datatype-pcollections
:提供对PCollections库的支持。
在将这些依赖项添加到项目中后,可以通过注册相应的模块来启用它们,例如使用ObjectMapper
的registerModule
方法。
更多datatype,参考 Third-party datatype modules (opens new window)
# 注册自己的Module
在使用Guava的Table类型时,发现并没有对于Table类反序列化的支持。可以自己来添加支持:
ObjectMapper OBJECT_MAPPER = new ObjectMapper();
// 添加序列化方案
OBJECT_MAPPER.registerModule(new GuavaModule());
// Guava的Table没有反序列化方案,使用自定义方案
SimpleModule simpleModule = new SimpleModule();
simpleModule.addDeserializer(Table.class, new TableDeserializer());
OBJECT_MAPPER.registerModule(simpleModule);
/**
* Guavua Table的反序列化方案
*/
public static class TableDeserializer extends JsonDeserializer<Table<?, ?, ?>> {
@Override
public Table<?, ?, ?> deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
final ImmutableTable.Builder<Object, Object, Object> tableBuilder = ImmutableTable.builder();
@SuppressWarnings("unchecked") final Map<Object, Map<Object, Object>> rowMap = jp.readValueAs(Map.class);
for (final Map.Entry<Object, Map<Object, Object>> rowEntry : rowMap.entrySet()) {
final Object rowKey = rowEntry.getKey();
for (final Map.Entry<Object, Object> cellEntry : rowEntry.getValue().entrySet()) {
final Object colKey = cellEntry.getKey();
final Object val = cellEntry.getValue();
tableBuilder.put(rowKey, colKey, val);
}
}
return tableBuilder.build();
}
}
# 11. Jackson的注解
在时间处理一节,我们已经领略了@JsonFormat
注解的使用。
Jackson提供了一系列的注解,用于控制序列化和反序列化过程中的行为。
下面对一些常用的注解进行说明:
@JsonIgnore
:- 作用: 标记某个字段或方法,在序列化和反序列化过程中忽略该属性。
- 示例代码:
public class Person { private String name; @JsonIgnore private int age; // 省略构造函数和其他方法 // getters and setters }
@JsonProperty
:- 作用: 指定字段或方法在序列化和反序列化中的名称。
- 示例代码:
public class Person { @JsonProperty("full_name") private String name; // 省略构造函数和其他方法 // getters and setters }
@JsonAnyGetter
:- 作用: 在序列化时将Java对象的动态属性作为键值对添加到JSON中。
- 示例代码:
public class Person { private Map<String, Object> properties = new HashMap<>(); public void setProperty(String key, Object value) { properties.put(key, value); } @JsonAnyGetter public Map<String, Object> getProperties() { return properties; } }
@JsonPropertyOrder
:- 作用: 指定序列化时属性的顺序。
- 示例代码:
@JsonPropertyOrder({ "name", "age", "email" }) public class Person { private String name; private int age; private String email; // 省略构造函数和其他方法 // getters and setters }
@JsonRawValue
:- 作用: 将字符串属性的原始值直接输出,而不进行额外的转义或引号添加。
- 示例代码:
public class Person { private String description; @JsonRawValue public String getDescription() { return description; } // 省略构造函数和其他方法 // setters }
@JsonAlias
:- 作用: 定义一个或多个替代名称,用于在反序列化过程中匹配JSON属性。
- 示例代码:
public class Person { @JsonAlias({ "firstName", "givenName" }) private String name; // 省略构造函数和其他方法 // getters and setters }
@JsonIgnoreType
:- 作用: 在序列化和反序列化过程中忽略指定的类型。
- 示例代码:
@JsonIgnoreType public class SecretDetails { private String password; private String secretKey; // 省略构造函数和其他方法 // getters and setters } public class Person { private String name; private int age; private SecretDetails secretDetails; // 省略构造函数和其他方法 // getters and setters }
Jackson还提供了很多其他注解,请参考 Jackson Annotations (opens new window) 和 Jackson Annotation Examples (opens new window)
# 12. 对其他数据格式的支持
除了默认的JSON格式外,Jackson提供了很多扩展模块。这些扩展模块提供了对各种数据格式的支持,如XML、YAML、CSV等。
Jackson的Data format modules提供了以下功能:
XML支持(Jackson XML Module): Jackson XML Module允许你将Java对象序列化为XML格式,或将XML反序列化为Java对象。它提供了与Jackson JSON库相似的API和注解,用于控制XML序列化和反序列化的行为。
YAML支持(Jackson YAML Module): Jackson YAML Module允许你将Java对象序列化为YAML格式,或将YAML反序列化为Java对象。它提供了简单而直观的API和注解,用于处理YAML数据。
CSV支持(Jackson CSV Module): Jackson CSV Module提供了对CSV(逗号分隔值)格式的支持。它允许你将Java对象序列化为CSV格式,或将CSV反序列化为Java对象。你可以通过注解或配置来控制CSV的映射规则。
Protobuf支持(Jackson Protobuf Module): Jackson Protobuf Module提供了对Google Protobuf格式的支持。它允许你将Protobuf消息序列化为JSON或XML,或将JSON或XML反序列化为Protobuf消息。
CBOR支持(Jackson CBOR Module): Jackson CBOR Module提供了对CBOR(Concise Binary Object Representation)格式的支持。它允许你将Java对象序列化为CBOR格式,或将CBOR反序列化为Java对象。CBOR是一种紧凑的二进制格式,适用于高效的数据传输和存储。
除了上述提到的模块,Jackson还提供了其他数据格式的支持扩展模块,如Smile(二进制JSON格式)和Properties文件格式。
使用这些Data format modules,你可以在Jackson中处理不同的数据格式,实现灵活的数据转换和交互。你可以根据需要选择相应的模块,并根据模块的文档和示例进行配置和使用。这些模块的引入通常需要添加相应的依赖项到项目的构建文件中。
以上的支持,你都可以在 Data format modules (opens new window) 中找到。
# 13. 其他
Jackson还提供了以下功能:
# 性能优化建议
# 1. ObjectMapper复用
// ❌ 错误:每次创建新实例
public String toJson(Object obj) {
return new ObjectMapper().writeValueAsString(obj);
}
// ✅ 正确:复用单例
private static final ObjectMapper MAPPER = new ObjectMapper();
public String toJson(Object obj) {
return MAPPER.writeValueAsString(obj);
}
# 2. 使用@JsonProperty减少反射
public class Person {
@JsonProperty("n")
private String name;
@JsonProperty("a")
private int age;
}
# 3. 避免使用JsonNode进行大量数据处理
JsonNode
适合动态JSON,但性能不如数据绑定- 大数据量时优先使用流式API(
JsonParser
/JsonGenerator
)
# 4. 合理配置Feature
// 关闭不需要的特性提升性能
mapper.configure(SerializationFeature.INDENT_OUTPUT, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
# 常见问题与解决方案
# 1. 循环引用
@JsonManagedReference // 父端
private List<Child> children;
@JsonBackReference // 子端
private Parent parent;
# 2. 日期格式问题
// 全局配置
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 或使用Java 8时间API
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
# 3. 大文件处理
// 使用流式API处理大文件
try (JsonParser parser = mapper.getFactory().createParser(new File("large.json"))) {
while (parser.nextToken() != JsonToken.END_OBJECT) {
// 逐个处理token
}
}
# 总结
本文全面介绍了Java中JSON处理的方方面面,重点讲解了Jackson库的使用:
- 基础知识:JSON格式、数据类型、应用场景
- 库对比:Jackson、Gson、Fastjson2的特点和选择
- 核心用法:对象映射、集合处理、复杂类型
- 高级特性:自定义序列化、注解体系、多格式支持
- 最佳实践:性能优化、常见问题解决
Jackson作为Java生态中最成熟的JSON处理库,提供了:
- 🚀 卓越的性能
- 🎯 完整的功能特性
- 🔧 灵活的配置选项
- 📚 丰富的扩展生态
掌握Jackson的使用,将极大提升你在Java项目中处理JSON数据的效率和质量。
祝你变得更强!