Jackson之ObjectMapper配置详解

1.Jackson基础信息

Jackson是当前用得比较广泛的序列化和反序列化 json 的 Java 开源框架。Jackson社区相对比较活跃,更新速度也比较快,从Github中的统计来看,Jackson 是最流行的 json 解析器之一 。 Spring MVC和Spring Boot的默认 json解析器便是 Jackson。Jackson 的核心模块由三部分组成:

  • jackson-core: 核心包,提供基于"流模式"解析的相关 API,它包括 JsonPaser 和 JsonGenerator。 Jackson内部实现正是通过高性能的流模式API的JsonGenerator 和 JsonParser 来生成和解析 json。
  • jackson-annotations: 注解包,提供标准注解功能。
  • jackson-databind: 数据绑定包, 提供基于"对象绑定" 解析的相关 API ( ObjectMapper ) 和"树模型" 解析的相关 API (JsonNode);基于"对象绑定" 解析的 API 和"树模型"解析的 API 依赖基于"流模式"解析的 API。

maven依赖

xml 复制代码
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.1</version>
</dependency>

2.ObjectMapper配置信息

Jackson提供了ObjectMapper来供程序员"定制化控制"序列化、反序列化的过程。objectMapper在调用writeValue()序列化 或 调用readValue()反序列化方法之前,往往需要设置 ObjectMapper 的相关配置信息 ,这些配置信息作用在 java 对象的所有属性上 ,表示在进行序列化和反序列化时进行一些特殊的处理。ObjectMapper的相关的配置属性主要在Feature这个枚举类里,Feature的源码和其作用如下:

scss 复制代码
public enum Feature {
	// Low-level I/O handling features:支持低级I/O操作特性
	/**
	 * 自动关闭源:默认true_启用(即:解析json字符串后,自动关闭输入流)
	 * 该特性,决定了解析器是否可以自动关闭非自身的底层输入源
	 * 1.禁用:应用程序将分开关闭底层的{@link InputStream} and {@link Reader}
	 * 2.启用:解析器将关闭上述对象,其自身也关闭,此时input终止且调用{@link JsonParser#close}
	 */
	AUTO_CLOSE_SOURCE(true),
 
	/**
	 * Support for non-standard data format constructs:支持非标准数据格式的json
	 * 该特性,决定了解析器是否可以解析含有Java/C++注释样式的JSON串(如:/*或//的注释符)
	 * 默认false:不解析含有注释符(即:true时能解析含有注释符的json串)
	 * 注意:该属性默认是false,因此必须显式允许,即通过JsonParser.Feature.ALLOW_COMMENTS 配置为true。
	 */
	ALLOW_COMMENTS(false),
 
	/**
	 * 默认false:不解析含有另外注释符
	 * 该特性,决定了解析器是否可以解析含有以"#"开头并直到一行结束的注释样式(这样的注释风格通常也用在脚本语言中)
	 * 注意:标准的json字符串格式没有含有注释符(非标准),然而则经常使用<br>
	 */
	ALLOW_YAML_COMMENTS(false),
 
	/**
	 * 这个特性决定parser是否能解析属性名字没有加双引号的json串(这种形式在Javascript中被允许,但是JSON标准说明书中没有)。
	 *(默认情况下,标准的json串里属性名字都需要用双引号引起来。比如:{age:12, name:"曹操"}非标准的json串,默认不能解析)
	 * 注意:由于JSON标准上需要为属性名称使用双引号,所以这也是一个非标准特性,默认是false的。
	 * 同样,需要设置JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES为true,打开该特性。
	 *
	 */
	ALLOW_UNQUOTED_FIELD_NAMES(false),
 
	/**
	 * 默认false:不解析含有单引号的字符串或字符
	 * 该特性,决定了解析器是否可以解析单引号的字符串或字符(如:单引号的字符串,单引号'\'')
	 * 注意:可作为其他可接受的标记,但不是JSON的规范
	 */
	ALLOW_SINGLE_QUOTES(false),
 
	/**
	 * 允许:默认false不解析含有结束语控制字符
	 * 该特性,决定了解析器是否可以解析结束语控制字符(如:ASCII<32,包含制表符\t、换行符\n和回车\r)
	 * 注意:设置false(默认)时,若解析则抛出异常;若true时,则用引号即可转义
	 */
	ALLOW_UNQUOTED_CONTROL_CHARS(false),
 
	/**
	 * 可解析反斜杠引用的所有字符,默认:false,不可解析
	 */
	ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER(false),
 
	/**
	 * 可解析以"0"为开头的数字(如: 000001),解析时则忽略0,默认:false,不可解析,若有则抛出异常
	 */
	ALLOW_NUMERIC_LEADING_ZEROS(false),
 
	/**
	 * 可解析非数值的数值格式(如:正无穷大,负无穷大,Integer或浮点数类型属性赋值NaN的JSON串)
	 * 该特性允许parser可以识别"Not-a-Number" (NaN)标识集合作为一个合法的浮点数。
	 * 默认:false,不能解析
	 */
	 ALLOW_NON_NUMERIC_NUMBERS(false),
 
	 /**
	  * 默认:false,不检测JSON对象重复的字段名,即:相同字段名都要解析
	  * true时,检测是否有重复字段名,若有,则抛出异常{@link JsonParseException}
	  * 注意:检查时,解析性能下降,时间超过一般情况的20-30%
	  */
	 STRICT_DUPLICATE_DETECTION(false),
 
	 /**
	  * 默认:false,底层的数据流(二进制数据持久化,如:图片,视频等)全部被output,若读取一个位置的字段,则抛出异常
	  * true时,则忽略未定义
	  */
	 IGNORE_UNDEFINED(false),
 
	 /**
	  * 默认:false,JSON数组中不解析漏掉的值,若有,则会抛出异常{@link JsonToken#VALUE_NULL}
	  * true时,可解析["value1",,"value3",]最终为["value1", null, "value3", null]空值作为null
	  */
	 ALLOW_MISSING_VALUES(false);
}

(1)忽略未知字段

默认情况下Jackson要求JSON字符串消息 和 Java类中的字段必须一一相对应,否则反序列解析JSON字符串时会报错。当然也可以通过配置Jackson的ObjectMapper属性使Jackson在反序列化时,忽略在 json字符串中存在但 Java 对象不存在的属性。

vbnet 复制代码
#例如:
#1)java对象属性
@Data
public class User implements Serializable {
    private Integer age;
    private String name;
}
 
#2)需要反序列化JSON字符串
public static void main(String[] args) throws Exception {
	ObjectMapper objectMapper = new ObjectMapper();
	String json = "{\"age\":10,\"name\":\"曹操\",\"class\":\"语文\"}";
	#会报错:因为json字符串的属性和java对象属性没有一一对应
	User user = objectMapper.readValue(json, User.class);
	System.out.println(user);
}
 
#异常:
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "class"
 
#3)解决办法:忽略未知字段 DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES默认是true。
	objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

(2)属性为NULL不被序列化

如果java对象的属性为NULL则不参与序列化,即java对象序列化后的json串里不出现属性为null的字段。该功能可以使用@JsonInclude注解,也可以设置objectMapper属性。

java 复制代码
#示例:
#1)java对象属性
@Data
public class User implements Serializable {
    private Integer age;
    private String name;
}
 
#2)序列化属性有为null的对象
public static void main(String[] args) throws Exception {
	ObjectMapper objectMapper = new ObjectMapper();
	User user = new User();
	user.setAge(10);
	String string = objectMapper.writeValueAsString(user);
	System.out.println(string);
}
#输出:json字段带null
{"age":10,"name":null}
 
#3)解决办法:关闭属性为NULL还序列化功能,默认是开启。
@JsonInclude(JsonInclude.Include.NON_NULL) 注解可以加到类或属性上,加到类上表示多所有属性都有效。objectMapper.setSerializationInclusion(Include.NON_NULL)

(3)对象属性为空时,默认序列化会失败

默认情况下ObjectMapper序列化没有属性的空对象时会抛异常。可以通过SerializationFeature.FAIL_ON_EMPTY_BEANS设置当对象没有属性时,让其序列化能成功,不抛异常。

php 复制代码
#示例:
#1)java对象属性:没有任何属性
@Data
public class User implements Serializable {
}
 
#2)默认序列化失败,会抛异常
public static void main(String[] args) throws Exception {
	ObjectMapper objectMapper = new ObjectMapper();
	#默认是true,空对象不让序列化
	//objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, true);
	User user = new User();
	String string = objectMapper.writeValueAsString(user); #会抛异常
	System.out.println(string);
}
#抛异常:com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class com.igetcool.common.model.User and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
 
#3)解决办法:关闭空对象不让序列化功能
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);

(4)json字符串值带反斜杠(""),默认反序列化会失败

当反序列化的JSON串里带有反斜杠时,默认objectMapper反序列化会失败,抛出异常Unrecognized character escape。可以通过Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER来设置当反斜杠存在时,能被objectMapper反序列化。

ini 复制代码
#示例:
#1)java对象属性
@Data
public class User implements Serializable {
    private Integer age;
    private String name;
}
 
#2)反序列化字符串带反斜杠,会抛异常。
public static void main(String[] args) throws Exception {
	ObjectMapper objectMapper = new ObjectMapper();
	#objectMapper默认是false
	objectMapper.configure(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, false);
	#name的值带反斜杠,默认情况下objectMapper解析器会反序列化失败
	String json = "{\"age\":10,\"name\":\"曹\\操\"}";
	User user = objectMapper.readValue(json, User.class);
	System.out.println(user);
}
#输出:com.fasterxml.jackson.databind.JsonMappingException: Unrecognized character escape '操' (code 25805 / 0x64cd)
 
#3)解决办法:设置解析能识别JSON串里的注释符
objectMapper.configure(Feature.ALLOW_BACKSLASH_ESCAPING_ANY_CHARACTER, true);

(5)反序列化json字符串中包含控制字符

Feature.ALLOW_UNQUOTED_CONTROL_CHARS该特性决定parser是否允许JSON字符串包含非引号控制字符(值小于32的ASCII字符,包含制表符\t、换行符\n和回车\r)。 如果该属性关闭,则如果遇到这些字符,则会抛出异常。

ini 复制代码
#默认情况下parser解析器是不能解析包含控制字符的json字符串,需要设置ALLOW_UNQUOTED_CONTROL_CHARS属性
#1)处理问题:JSON串里属性或属性值包含控制字符,解析器能解析。
{
	"age": 12,
	"name": "曹操\n"
}
 
#2)示列
public static void main(String[] args) throws Exception {
	ObjectMapper objectMapper = new ObjectMapper();
	//开启单引号解析
	objectMapper.configure(Feature.ALLOW_SINGLE_QUOTES, true);
	//开启JSON字符串包含非引号控制字符的解析(\n换行符)
	objectMapper.configure(Feature.ALLOW_UNQUOTED_CONTROL_CHARS, true);
	String json = "{'age':12, 'name':'曹操\n'}";
	User user = objectMapper.readValue(json, User.class);
	System.out.println(user);
}
#输出:输出有换行效果
User(age=12, name=曹操)

(6)时间格式化

Jackson对时间(Date)序列化的转换格式默认是时间戳,可以取消时间的默认时间戳转化格式;默认时间戳转化格式取消后在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ,同时需要设置要展现的时间格式。

java 复制代码
#(1)Jackson对时间(Date)序列化的转换格式默认是时间戳
public static void main(String[] args) throws JsonProcessingException {
	ObjectMapper mapper = new ObjectMapper();
	//WRITE_DATES_AS_TIMESTAMPS属性值默认就是true
	//mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
	String date = mapper.writeValueAsString(new Date());
	System.out.println("默认是时间戳格式:" + date);
}
//输出:
默认是时间戳格式:1605848842390
 
#(2)取消时间的默认时间戳转化格式后,再序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ
public static void main(String[] args) throws JsonProcessingException {
	ObjectMapper mapper = new ObjectMapper();
	//WRITE_DATES_AS_TIMESTAMPS属性值默认就是true
	mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
	String date = mapper.writeValueAsString(new Date());
	System.out.println("默认时格式:" + date);
}
//输出:
默认时格式:"2020-11-20T05:12:22.868+0000"
 
#(3)取消Jackson时间的默认时间戳转化格式,并设置需要展现的时间格式
public static void main(String[] args) throws JsonProcessingException {
    ObjectMapper mapper = new ObjectMapper();
    //取消时间的转化格式默认是时间戳,可以取消,同时需要设置要表现的时间格式
    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
 
    String date = mapper.writeValueAsString(new Date());
    System.out.println("指定时间时格式:" + date);
}
//输出:
指定时间时格式:"2020-11-20 13:14:56"

Springboot使用的默认json解析框架是jackjson框架,在格式化Model时对Date属性指定时间格式方式有以下三种方法:

ini 复制代码
#(1)配置文件:将spring的jackson日期格式写在配置文件中即可。
spring:
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
    serialization:
      write-dates-as-timestamps: false
 
#或者写成以下格式(主要取决于配置文件是什么格式的)
 
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
spring.jackson.serialization.write-dates-as-timestamps=false
 
#(2)注解:在实体Date类型的字段上使用@JsonFormat注解格式化日期
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT + 8")
private Date createTime;
 
#(3)设置ObjectMapper属性:通过下面方式取消timestamps形式,并设置时间格式
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
相关推荐
机器之心1 小时前
图学习新突破:一个统一框架连接空域和频域
人工智能·后端
.生产的驴2 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
顽疲2 小时前
springboot vue 会员收银系统 含源码 开发流程
vue.js·spring boot·后端
机器之心2 小时前
AAAI 2025|时间序列演进也是种扩散过程?基于移动自回归的时序扩散预测模型
人工智能·后端
hanglove_lucky3 小时前
本地摄像头视频流在html中打开
前端·后端·html
皓木.5 小时前
(自用)配置文件优先级、SpringBoot原理、Maven私服
java·spring boot·后端
i7i8i9com5 小时前
java 1.8+springboot文件上传+vue3+ts+antdv
java·spring boot·后端
秋意钟5 小时前
Spring框架处理时间类型格式
java·后端·spring
我叫啥都行5 小时前
计算机基础复习12.22
java·jvm·redis·后端·mysql
Stark、5 小时前
【Linux】文件IO--fcntl/lseek/阻塞与非阻塞/文件偏移
linux·运维·服务器·c语言·后端