Java JSON处理
# 前言
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它以易于阅读和编写的文本格式表示结构化数据。
JSON最初由Douglas Crockford在2001年提出,并且广泛应用于Web应用程序之间的数据交换。
JSON具有以下特点:
- 简洁性:JSON使用一种简单的文本格式,易于阅读和编写。
- 可读性:JSON的结构对于开发人员来说非常直观和易于理解。
- 跨平台和语言支持:由于JSON是一种通用的数据交换格式,它可以在不同平台和编程语言之间进行交换和解析。
- 平台无关性:JSON与特定的编程语言无关,因此可以在多种编程语言中使用。
- 扩展性:JSON支持嵌套和复杂的数据结构,可以表示更大规模的数据。
- Web应用程序支持:由于JSON与JavaScript密切相关,它在Web应用程序中广泛使用,特别是在与服务器进行数据交换时。
JSON在许多应用程序中被广泛使用,特别是在Web开发、API设计和移动应用程序中。它是一种简单、灵活和通用的数据交换格式,被广泛接受并得到支持。
JSON采用键值对的方式组织数据,使用简单的数据类型包括字符串、数字、布尔值、数组、对象和null。这使得JSON非常适合于表示复杂的数据结构。
比如下面的JSON对象,它包含一个名为John的人的信息,包括姓名、年龄、城市、爱好、是否是学生和地址等信息。
{
"name": "John",
"age": 30,
"city": "New York",
"hobbies": ["reading", "coding", "traveling"],
"isStudent": false,
"address": {
"street": "123 Main St",
"zip": "10001"
}
}
# Java中的JSON应用场景
在Java中,JSON的使用场景非常广泛。
下面是一些常见的Java中使用JSON的场景:
- 数据交换:JSON作为一种通用的数据交换格式,可以在不同系统、不同编程语言之间进行数据交换。在Java中,可以使用JSON来序列化Java对象为JSON格式,并将其发送给其他系统或服务。反之,也可以将收到的JSON数据反序列化为Java对象。
- Web服务:在Web开发中,JSON经常用于在客户端和服务器之间传输数据。Java中的Web服务框架(如Spring MVC、JAX-RS)通常支持将Java对象转换为JSON格式的响应,以及将接收到的JSON数据转换为Java对象。
- RESTful API:基于RESTful架构的API通常使用JSON作为数据交换格式。Java中的RESTful框架(如Spring Boot、JAX-RS)可以方便地将Java对象转换为JSON格式,并与客户端进行交互。
- 数据存储:有时候需要将Java对象持久化到数据库或文件系统中。在这种情况下,可以将Java对象转换为JSON格式,并将其存储为文本文件或将其作为字段保存在数据库表中。
- 配置文件:JSON也可以用作配置文件的格式。许多Java应用程序使用JSON格式来定义和配置应用程序的各种设置和选项。
- 测试数据:在编写单元测试或集成测试时,可以使用JSON格式的数据来模拟外部服务的响应或输入数据。这样可以更灵活地定义测试数据,并方便地与Java对象进行序列化和反序列化。
# Java中的JSON库概述
在Java中,有许多第三方库可以用于处理JSON,最流行的库主要是Gson、Jackson、Fastjson等。
Spring MVC的默认JSON库使用了Jackson,Jackson主要有以下优势:
- 性能优势:Jackson具有优秀的性能,尤其是在解析和生成JSON时。它的性能要好于其他类似的Java JSON解析库,如Gson和Json-smart。
- 灵活性和可扩展性:Jackson提供了一个丰富的特性集,可以很容易地解析,生成和修改JSON。它支持自定义序列化和反序列化,以及自定义的数据类型映射和处理器。
- 多种格式支持:虽然Jackson主要用于处理JSON,但它还支持其他数据格式,如XML,YAML,CSV和Smile。这使得Jackson能够方便地处理不同格式的数据,而无需更改API。
- 注解支持:Jackson支持多种注解,这使得开发人员可以在数据类中直接定义JSON序列化和反序列化规则。这提高了代码的可读性和可维护性。
- 完整的文档和社区支持:Jackson库的文档齐全,使用广泛,有活跃的社区和众多的实用教程,这使得开发人员可以迅速学习和解决问题。
- 模块化:Jackson具有模块化的设计,这使得开发人员可以轻松地集成其它功能和扩展库,满足特定的应用需求。
本篇文章主要介绍Jackson的使用,但在这之前,对于Gson和Fastjson做一个简单的介绍。
# Gson
Gson是Google开发的一个用于序列化和反序列化Java对象的库。基于Apache许可面向公众,广泛应用在数据存储、网络传输等方面。Gson提供了一种简单的方式来将Java对象转换为相应的JSON格式字符串,并且可以将JSON字符串解码反序列化成Java对象。
关于Gson的使用,参考 官方教程 (opens new window)
# Fastjson
Fastjson是阿里巴巴开发的一个基于Java语言实现的高性能、功能强大的JSON库。它主要用于将Java对象进行序列化和反序列化。Fastjson 的主要设计目标是快速、安全、清晰,使开发者能够迅速掌握并从容应对大量的 JSON 数据的传输、解析和存储等操作。
关于Fastjson的使用,参考 官方教程 (opens new window)
# Fastjson2
Dubbo 3.2开始,Dubbo默认使用Fastjson2作为JSON序列化和反序列化的实现。Fastjson2是Fastjson的升级版,它在Fastjson的基础上进行了重构,提供了更好的性能和更丰富的功能。
Fastjson2最大的升级是其性能和安全性,关于和其他库的性能对比参考: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对象
在使用Jackson之前,我们需要创建一个ObjectMapper对象。ObjectMapper是Jackson库的核心类,用于执行JSON的读取、写入和转换操作。可以通过以下方式创建ObjectMapper对象:
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper objectMapper = new ObjectMapper();
需要注意的是,ObjectMapper是一个重对象,因此在应用程序中应该尽量创建一个共享的ObjectMapper对象,避免创建多个ObjectMapper对象。
ObjectMapper是线程安全的,因此可以在多线程环境下共享ObjectMapper对象。
# 3. JSON与Java对象的相互转换
Jackson提供了将JSON数据与Java对象相互转换的功能。
先定义一个Java类:
record MyClass(String name, Integer 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还提供了以下功能:
# 总结
本文介绍了Java中处理JSON的相关知识和常用库,重点介绍了Jackson库的使用。
通过本文的学习,你将能够在Java项目中灵活处理JSON数据,使用Jackson库实现高效的JSON序列化和反序列化。无论是简单的对象转换还是复杂的数据处理,Jackson都提供了丰富的功能和灵活的配置选项,满足各种需求。
希望本文对你理解和使用Java中的JSON处理有所帮助,为你的开发工作提供指导和参考。
祝你变得更强!