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 小时前
Unsafe魔法类深度解析:Java底层操作的终极指南
后端
神奇小汤圆1 小时前
浅析二叉树、B树、B+树和MySQL索引底层原理
后端
文艺理科生2 小时前
Nginx 路径映射深度解析:从本地开发到生产交付的底层哲学
前端·后端·架构
千寻girling2 小时前
主管:”人家 Node 框架都用 Nest.js 了 , 你怎么还在用 Express ?“
前端·后端·面试
南极企鹅2 小时前
springBoot项目有几个端口
java·spring boot·后端
Luke君607972 小时前
Spring Flux方法总结
后端
define95272 小时前
高版本 MySQL 驱动的 DNS 陷阱
后端
忧郁的Mr.Li2 小时前
SpringBoot中实现多数据源配置
java·spring boot·后端
暮色妖娆丶3 小时前
SpringBoot 启动流程源码分析 ~ 它其实不复杂
spring boot·后端·spring
Coder_Boy_3 小时前
Deeplearning4j+ Spring Boot 电商用户复购预测案例中相关概念
java·人工智能·spring boot·后端·spring