1. 首页
  2. 后端

Jackson之ObjectMapper配置详解

  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依赖

<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的源码和其作用如下:

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 对象不存在的属性。

#例如:
#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属性。

#示例:
#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设置当对象没有属性时,让其序列化能成功,不抛异常。

#示例:
#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反序列化。

#示例:
#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)。 如果该属性关闭,则如果遇到这些字符,则会抛出异常。

#默认情况下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,同时需要设置要展现的时间格式。

#(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属性指定时间格式方式有以下三种方法:

#(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"));

原文链接: https://juejin.cn/post/7364698043910225931

文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17721.html

QR code