在 Java 开发中,Jackson 是最常用的 JSON 处理库之一。ObjectMapper
是 Jackson 的核心类,用于在 Java 对象与 JSON 数据之间进行转换。通过 configure()
方法,我们可以配置 ObjectMapper
的各种特性,以满足不同的需求。本文将详细讲解 ObjectMapper.configure()
方法可以配置的参数及其作用。
ObjectMapper.configure()
方法介绍
ObjectMapper.configure()
方法允许开发者在序列化和反序列化过程中控制 Jackson 的行为。该方法接收两个参数:
- 特性枚举:决定配置的具体行为。
- 布尔值 :启用 (
true
) 或禁用 (false
) 特性。
Jackson 提供了多种特性,可以通过不同的枚举来控制。主要包括以下几类:
- SerializationFeature
- DeserializationFeature
- JsonParser.Feature
- JsonGenerator.Feature
- MapperFeature
- [StreamReadFeature 和 StreamWriteFeature](#StreamReadFeature 和 StreamWriteFeature)
SerializationFeature
SerializationFeature
用于控制 Java 对象序列化为 JSON 时的行为。常用特性包括:
-
WRITE_DATES_AS_TIMESTAMPS
:- 默认值 :
true
- 说明 :将日期(如
java.util.Date
和java.time
类)序列化为时间戳(长整型)。如果设置为false
,日期将以 ISO-8601 字符串格式输出。
- 默认值 :
-
INDENT_OUTPUT
:- 默认值 :
false
- 说明:启用后,输出的 JSON 将使用缩进格式,使其更加美观(即 pretty-print)。
- 默认值 :
-
FAIL_ON_EMPTY_BEANS
:- 默认值 :
true
- 说明 :序列化空的 Java 对象(没有任何字段或属性)时会引发异常。如果设置为
false
,空对象会被序列化为空的 JSON 对象{}
。
- 默认值 :
-
WRAP_ROOT_VALUE
:- 默认值 :
false
- 说明 :启用后,序列化的 JSON 将根值包裹在一个键中,该键的名称由类的
@JsonRootName
注解决定。
- 默认值 :
DeserializationFeature
DeserializationFeature
用于控制 JSON 反序列化为 Java 对象时的行为。常见的配置有:
-
FAIL_ON_UNKNOWN_PROPERTIES
:- 默认值 :
true
- 说明 :反序列化过程中,如果遇到未知的 JSON 属性将会引发异常。如果设置为
false
,这些属性将被忽略。
- 默认值 :
-
ACCEPT_SINGLE_VALUE_AS_ARRAY
:- 默认值 :
false
- 说明 :允许将 JSON 中的单一值反序列化为 Java 数组或集合,例如将
"value"
反序列化为["value"]
。
- 默认值 :
-
UNWRAP_ROOT_VALUE
:- 默认值 :
false
- 说明:启用后,反序列化的 JSON 必须包含一个包裹根的值,并且只反序列化该根值的内容。
- 默认值 :
-
READ_UNKNOWN_ENUM_VALUES_AS_NULL
:- 默认值 :
false
- 说明 :如果设置为
true
,在反序列化枚举类型时,遇到未知的枚举值将被映射为null
。
- 默认值 :
JsonParser.Feature
JsonParser.Feature
控制 JSON 解析器 (JsonParser
) 的行为,以下是一些常见的配置:
-
ALLOW_COMMENTS
:- 默认值 :
false
- 说明 :允许并忽略 JSON 中的注释(
//
或/* ... */
)。
- 默认值 :
-
ALLOW_SINGLE_QUOTES
:- 默认值 :
false
- 说明 :允许 JSON 字符串使用单引号(
'
)而不是标准的双引号("
)。
- 默认值 :
-
ALLOW_UNQUOTED_FIELD_NAMES
:- 默认值 :
false
- 说明:允许 JSON 中的字段名没有引号。
- 默认值 :
-
ALLOW_TRAILING_COMMA
:- 默认值 :
false
- 说明:允许在 JSON 对象和数组的末尾存在多余的逗号。
- 默认值 :
JsonGenerator.Feature
JsonGenerator.Feature
控制 JSON 生成器 (JsonGenerator
) 的行为,以下是一些常见的配置:
-
WRITE_NUMBERS_AS_STRINGS
:- 默认值 :
false
- 说明:所有数字将作为字符串输出。
- 默认值 :
-
ESCAPE_NON_ASCII
:- 默认值 :
false
- 说明 :非 ASCII 字符将以 Unicode 转义序列(例如
\uXXXX
)输出。
- 默认值 :
-
QUOTE_FIELD_NAMES
:- 默认值 :
true
- 说明:字段名将被加引号,这符合标准 JSON 规范。
- 默认值 :
MapperFeature
MapperFeature
控制了 ObjectMapper
的整体映射行为,包括以下一些常见配置:
-
AUTO_DETECT_FIELDS
:- 默认值 :
true
- 说明 :自动检测并序列化/反序列化所有可见的字段(即使没有
@JsonProperty
注解)。
- 默认值 :
-
DEFAULT_VIEW_INCLUSION
:- 默认值 :
true
- 说明:默认情况下,所有字段都会包含在序列化中,即使它们不属于特定视图。
- 默认值 :
-
USE_ANNOTATIONS
:- 默认值 :
true
- 说明 :如果设置为
true
,ObjectMapper
会尊重类和字段上的 Jackson 注解(如@JsonProperty
、@JsonIgnore
等)。
- 默认值 :
StreamReadFeature 和 StreamWriteFeature
StreamReadFeature
和 StreamWriteFeature
是在 Jackson 2.12 版本中引入的,用于控制流式读取和写入的行为。例如:
-
INCLUDE_SOURCE_IN_LOCATION
(StreamReadFeature):- 默认值 :
false
- 说明 :如果设置为
true
,JsonParser
将在抛出JsonParseException
时包含源内容的引用(例如导致错误的 JSON 片段),这有助于调试。
- 默认值 :
-
STRICT_DUPLICATE_DETECTION
(StreamWriteFeature):- 默认值 :
false
- 说明 :在检测到重复的 JSON 对象字段时,
JsonGenerator
将抛出异常。
- 默认值 :
如何使用 configure()
方法配置特性
下面是一些使用 configure()
方法配置 ObjectMapper
特性的示例:
java
ObjectMapper mapper = new ObjectMapper();
// 启用或禁用特性
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
mapper.configure(MapperFeature.AUTO_DETECT_FIELDS, true);
mapper.configure(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature(), true);
示例:自定义 JSON 序列化和反序列化行为
假设你有一个 User
类,需要对其进行 JSON 序列化和反序列化。要求包括:
- 日期字段应该以 ISO-8601 格式输出,而不是时间戳。
- 未知的 JSON 属性在反序列化时应该被忽略。
- 输出的 JSON 需要格式化,便于阅读(缩进显示)。
首先,定义一个简单的 User
类:
java
import java.util.Date;
public class User {
public String name;
public int age;
public Date registeredDate;
// Constructors, getters, and setters
public User(String name, int age, Date registeredDate) {
this.name = name;
this.age = age;
this.registeredDate = registeredDate;
}
public User() {
}
}
配置 ObjectMapper
特性
接下来,使用 configure()
方法配置 ObjectMapper
,以满足上述要求:
java
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.DeserializationFeature;
import java.text.SimpleDateFormat;
public class ObjectMapperExample {
public static void main(String[] args) throws Exception {
// 创建 ObjectMapper 实例
ObjectMapper mapper = new ObjectMapper();
// 配置特性
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // 日期以 ISO-8601 格式输出
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略未知属性
mapper.configure(SerializationFeature.INDENT_OUTPUT, true); // 格式化输出
// 也可以设置日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
// 创建一个 User 对象
User user = new User("Alice", 30, new Date());
// 序列化 User 对象为 JSON
String jsonString = mapper.writeValueAsString(user);
System.out.println("序列化的 JSON:\n" + jsonString);
// 假设从 JSON 字符串反序列化为 User 对象
String inputJson = "{\"name\":\"Bob\",\"age\":25,\"registeredDate\":\"2024-09-02\",\"extraField\":\"ignored\"}";
User newUser = mapper.readValue(inputJson, User.class);
System.out.println("反序列化后的 User 对象:\n" + newUser.name + ", " + newUser.age + ", " + newUser.registeredDate);
}
}
结果输出
运行上面的代码,你将得到以下输出:
序列化的 JSON(日期以 ISO-8601 格式,格式化输出):
json
{
"name" : "Alice",
"age" : 30,
"registeredDate" : "2024-09-02"
}
反序列化后的 User
对象 (忽略未知属性 extraField
):
yaml
Bob, 25, Mon Sep 02 00:00:00 UTC 2024
代码解析
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
:- 这个配置使得日期字段在序列化时以 ISO-8601 格式输出,而不是默认的时间戳。
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
:- 通过这个配置,Jackson 在反序列化时会忽略 JSON 中的未知属性,避免抛出异常。
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
:- 启用 JSON 格式化输出,使其更具可读性,特别是在调试或日志中使用时。
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
:- 这进一步定制了日期的输出格式,使日期字段严格遵循
yyyy-MM-dd
格式。
- 这进一步定制了日期的输出格式,使日期字段严格遵循
异常排查
在使用 Jackson 进行 JSON 序列化和反序列化时,可能会遇到各种异常。以下是一些常见的异常及其排查方法:
1. JsonParseException
描述 :JsonParseException
通常发生在解析 JSON 时,出现了格式错误、意外结束或其他问题。
常见原因:
- JSON 数据不完整,导致解析中途结束。
- JSON 格式不正确,例如缺少大括号或方括号。
- 遇到非法字符或不被支持的 JSON 语法。
解决方法:
- 验证 JSON 数据的完整性,确保数据在传输或存储过程中没有被截断。
- 使用在线 JSON 校验工具检查 JSON 数据的格式。
- 捕获异常,并在日志中记录错误位置和具体内容,帮助调试。Jackson 2.12 及以上版本提供了
StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION
特性。启用此特性后,抛出的JsonParseException
中将包含源内容的位置引用(例如引发错误的 JSON 片段),这有助于快速定位问题。
java
try {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION.mappedFeature(), true);
MyObject obj = mapper.readValue(jsonString, MyObject.class);
} catch (JsonParseException e) {
System.err.println("JSON parsing failed: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
2. JsonMappingException
描述 :JsonMappingException
是在映射 JSON 数据到 Java 对象时发生的异常,通常与字段类型不匹配或缺少必要的映射信息有关。
常见原因:
- JSON 字段的类型与 Java 对象的字段类型不匹配。
- JSON 数据中缺少某个必须存在的字段。
- Java 对象中的类或字段没有相应的无参构造函数或访问器(getter/setter)。
解决方法:
- 检查 JSON 数据和 Java 对象之间的映射关系,确保字段名和数据类型一致。
- 如果某些字段是可选的,可以使用
@JsonIgnoreProperties(ignoreUnknown = true)
注解忽略未知字段。 - 确保 Java 对象中包含无参构造函数和必要的访问器。
java
try {
ObjectMapper mapper = new ObjectMapper();
MyObject obj = mapper.readValue(jsonString, MyObject.class);
} catch (JsonMappingException e) {
System.err.println("JSON mapping failed: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
3. JsonGenerationException
描述 :JsonGenerationException
在生成 JSON 数据时出现,通常与不合法的 JSON 内容有关。
常见原因:
- 试图将一个不支持序列化的对象写入 JSON。
- 出现递归引用,导致生成无限循环的 JSON 结构。
解决方法:
- 检查并确保所有对象都可以被序列化。如果需要忽略某些字段,可以使用
@JsonIgnore
注解。 - 对于递归引用的情况,可以使用
@JsonIdentityInfo
或@JsonManagedReference
和@JsonBackReference
注解来处理。
java
try {
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(myObject);
} catch (JsonGenerationException e) {
System.err.println("JSON generation failed: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
4. InvalidDefinitionException
描述 :InvalidDefinitionException
是一种在序列化或反序列化过程中定义不正确导致的异常,通常与类型定义、注解配置等问题相关。
常见原因:
- 使用了不受支持的类型或缺少序列化/反序列化定义。
- 缺少适当的构造函数或无法访问的字段。
解决方法:
- 确保类的定义正确,所有需要序列化/反序列化的字段都有适当的访问权限。
- 检查类上是否正确使用了 Jackson 的注解,例如
@JsonCreator
、@JsonProperty
等。
java
try {
ObjectMapper mapper = new ObjectMapper();
MyObject obj = mapper.readValue(jsonString, MyObject.class);
} catch (InvalidDefinitionException e) {
System.err.println("Invalid definition: " + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
5. UnrecognizedPropertyException
描述 :UnrecognizedPropertyException
发生在反序列化过程中,JSON 中包含了 Java 对象中未定义的字段。
常见原因:
- JSON 数据包含不属于 Java 对象的额外字段。
- JSON 数据结构更新后,Java 类未及时更新以反映这些更改。
解决方法:
- 可以通过设置
DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
为false
来忽略未知字段。 - 或者使用
@JsonIgnoreProperties(ignoreUnknown = true)
注解来忽略这些未知属性。
java
try {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
MyObject obj = mapper.readValue(jsonString, MyObject.class);
} catch (UnrecognizedPropertyException e) {
System.err.println("Unrecognized property in JSON: " + e.getPropertyName());
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
结语
Jackson 的 ObjectMapper
是一个功能强大且灵活的工具,通过正确配置特性,你可以精确控制 JSON 序列化和反序列化的行为。然而,在实际使用过程中,难免会遇到各种异常。了解并掌握这些异常的排查方法,可以帮助你更快地定位问题,并提高开发效率。