Jackson 是当前用的比较广泛的,用来序列化和反序列化 json 的 Java 的开源框架。Jackson 社区相对比较活跃,更新速度也比较快。
Spring MVC 的默认 json 解析器便是 Jackson。(目前最新稳定版本: 2.13.4
)
Jackson 的核心模块由三部分组成,一般是引入三个包并保证他们 3 的版本一样:
jackson-core
,核心包,提供基于"流模式"解析的相关 API,它包括 JsonPaser 和 JsonGenerator。 Jackson 内部实现正是通过高性能的流模式 API 的 JsonGenerator 和 JsonParser 来生成和解析 json。jackson-annotations
,注解包,提供标准注解功能;jackson-databind
,数据绑定包, 提供基于"对象绑定" 解析的相关 API ( ObjectMapper ) 和"树模型" 解析的相关 API (JsonNode);基于"对象绑定" 解析的 API 和"树模型"解析的 API 依赖基于"流模式"解析的 API。<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.13.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4</version>
</dependency>
Jackson 中最最最常用的 API 就是基于"对象绑定" 的类 :
ObjectMapper
,它位于 jackson-databind中,你可以只导入这一个包
object 英文含义为对象,mappper 英文含义为转换/映射。组合起来,ObjectMapper 就是 对象转换
ObjectMapper 封装提供了 一系列对象转换 的方法,对象转换将其细分后又分为 序列化 和 反序列化 ,在 Java 当中, 序列化 与 Java-SDK 提供得接口 Serializable 密切相关。
举例说明,在网络通信中,我们的 Http 接口一般要求的都是传递字符串, 而在开发时,我们书写的是 class 这种数据类型,所以要将 class 数据转换为字符串。
@SpringBootTest
class UserControllerTest {
@Test
public void useObjectMapper() throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String userString = "{\"phone\": \"13366668888\", \"pwd\" : \"123456\"}";
UserEntity user = objectMapper.readValue(userString, UserEntity.class);
System.out.println(user);
System.out.println("=============");
String newString = objectMapper.writeValueAsString(user);
System.out.println(newString);
}
}
class UserEntity implements Serializable {
private String phone;
private String pwd;
}
那什么叫做序列化和反序列化呢?其实就是从对象到字符串,那么称之为正向转换,也就是序列化,反之则是反序列化。
序列化 这个名称初听让人很懵逼,什么玩意儿啊这是?所以为什么要取这么个名字?
数据传输后都需要存储在磁盘上,对于磁盘来说, 数据需要按照顺序和位置存储在指定位置,下次才能再读取出来。所以对于数据而言,需要提前将其顺序排列好,这样便于存入磁盘中。
所以序列化的含义就是有序的队列,化就是转化,将数据转化为有顺序的队列。 一切为了存储,一切为了存储,一切为了存储。
技术从国外传入时,译者起了这么个名字流传甚广,从此成为定例。其实也可以叫做转换,转化,变化都可以。
其实剩下的内容很简单了,就是看看 ObjectMapper 为我们提供了哪些方法,看看怎么使用它们。方法有很多,简单列举几个。
ObjectMapper objectMapper = new ObjectMapper();
File file = new File("data/car.json");
Car car = objectMapper.readValue(file, Car.class);
String jsonObject = "{\"brand\":\"ford\", \"doors\":5}";
ObjectMapper objectMapper = new ObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(jsonObject, new TypeReference<Map<String,Object>>(){});
String jsonArray = "[{\"brand\":\"ford\"}, {\"brand\":\"Fiat\"}]";
ObjectMapper objectMapper = new ObjectMapper();
Car[] cars2 = objectMapper.readValue(jsonArray, Car[].class);
还有很多方法,大同小异,你只需要知道 jackson 提供了 从多种数据类型以及文件中读取数据并与 java 对象相互转换得方法
,需要得时候查阅文档
常见的若干特殊情况:
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true);
StdDeserializer
比如,我现在要序列化的数据是:
{
"phone": "13911112222",
"pwd": "123456",
"role": {
"roleId": "666"
}
}
我希望将数据序列化为这两个实体:
@Data
public class UserEntity implements Serializable {
private String phone;
private String pwd;
public UserEntity(String phone,String password,RoleEntity role) {
this.phone = phone;
this.password = password;
this.role = role;
}
}
@Data
class RoleEntity implements Serializable {
private Integer roleId;
private String roleName;
}
我希望在序列化 User 时,自动创建其角色名称为:freedom。那么整个自定义序列化的流程是:
@Test
public void testObjectMapperDefine() throws JsonProcessingException {
// 1. 创建 objectMapper 对象
ObjectMapper objectMapper = new ObjectMapper();
// 2. 创建 module 对象 (SimpleModule 类似于可安装到 ObjectMapper的插件)
SimpleModule module = new SimpleModule();
// 3. 对 module 这个插件先进行设置。设置此插件将要作用于 UserEntity,并指定目标的序列化方法(序列化方法稍后由我们自定义)
module.addDeserializer(UserEntity.class, new DefineMySerialHandle());
// 4. module 插件一切搞定之后,注册到 objectMapper 中
objectMapper.registerModule(module);
// 现在当我们使用 objectMapper 来对数据进行序列化/反序列化时,符合 UserEntity 这个类的数据就会被自定义的序列化方法操作
// 验证执行
String userString = "{\"phone\": \"13911112222\", \"pwd\" : \"123456\",\"role\":{\"roleId\":\"666\"}}";
UserEntity user = objectMapper.readValue(userString, UserEntity.class);
// 反序列化后得到的User实体,可以看到它的角色名,也就是 roleName 属性被设置为了 freedom
System.out.println(user);
}
自定义反序列化方法:
// 泛型指定 序列化目标
public class DefineMySerialHandle extends StdDeserializer<UserEntity> {
public DefineMySerialHandle() {
this(null);
}
protected DefineMySerialHandle(Class<?> vc) {
super(vc);
}
@Override
public UserEntity deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
String phone = node.get("phone").asText();
String pwd = node.get("pwd").asText();
RoleEntity role = new RoleEntity();
role.setRoleName("freedom");
// 反序列化,首要核心目的是 得到序列化目标 UserEntity,因此必须返回 UserEntity 类型
// 而将实体Role的字段roleName设置 freedom 是 附带的随手操作
return new UserEntity(phone, pwd, role);
}
}
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Item.class, new ItemDeserializer());
mapper.registerModule(module);
Item readValue = mapper.readValue(json, Item.class);
到这里已经足够应付工作中的几乎所有场景了,你唯一需要做的是,将 ObjectMapper 提供的 API 自己挑 10 个调用测试一下,足以用一辈子.
在 springboot 项目中处理全局异常时,通常会使用到 Jasckson.此处其实就是将 Java 中的 Map 类型数据转换为 Json 字符串。
package com.mock.water.core.exception;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@Autowired
private ObjectMapper objectMapper;
@ExceptionHandler(value= {MethodArgumentNotValidException.class , BindException.class})
public String onException(Exception e) throws JsonProcessingException {
BindingResult bindingResult = null;
if (e instanceof MethodArgumentNotValidException) {
bindingResult = ((MethodArgumentNotValidException)e).getBindingResult();
} else if (e instanceof BindException) {
bindingResult = ((BindException)e).getBindingResult();
}
Map<String,String> errorMap = new HashMap<>(16);
assert bindingResult != null;
bindingResult.getFieldErrors().forEach((fieldError)->
errorMap.put(fieldError.getField(),fieldError.getDefaultMessage())
);
return objectMapper.writeValueAsString(errorMap);
}
}
Tips,思考一下,在这个示例中使用 @Autowired 注入 ObjectMapper 对象来使用,如果要自定义一个处理模块,如何添加到里面呢?
Jackson 对时间进行格式化时,默认时区为UTC
。
JVM 在运行时采用的是操作系统时区,部暑在Linux系统中后,默认时区是 GMT+8
.当然对于我们本地开发时是UTC
.你可以通过打开 系统 - 日期与时间
来查看.
所以需要让他们统一,这样在格式化时间时才正常。在 application.yml 中配置如下:
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
default-property-inclusion: NON_NULL
------ 如果文章对你有用,感谢右上角 >>>点赞 | 收藏 <<<