# Jackson
Jackson 是 SpringMVC 默认使用的 json 库。
# 1. 基本使用
jackson 提供了 writeValueAsString 方法和 readValue 方法这两个直接用于生成和解析的方法,前者实现序列化,后者实现了反序列化。
public class User {
private String name;
private int age;
private String emailAddress;
// 省略 getter/setter
}
# Object to JSON-String
ObjectMapper mapper = new ObjectMapper();
User user = new User("怪盗kidou", 24);
String jsonString = mapper.writeValueAsString(user);
# JSON-String to Object
ObjectMapper mapper = new ObjectMapper();
String jsonString = "{\"name\":\"怪盗kidou\",\"age\":24}";
User user = mapper.readValue(str, User.class);
jackson 被认为功能丰富而强大的原因是,它除了提供『对象 <-> 字符串』之间的相互转换,还提供了『对象 <-> 各种流』之间的转换,不过,我们没用上。
# 2. @JsonProperty 注解
默认/一般情况下,JSON 字段中的名字和类的属性名是一致的。
但是也有不一致的情况,因为本身 驼峰命名法(如 Java)和 下划线命名法(如 C)本身就是两大命名规则『流派』。
对类的属性使用 @JsonProperty 注解,可以重新指定与该属性对应的 JSON 字符串中的名字。
@JsonProperty("email_address")
private String emailAddress;
你甚至可以重新命名为另一个看起来毫不相关的名字:
@JsonProperty("xxx")
public String emailAddress;
# 3. 数组的序列化和反序列化
数组的序列化和反序列化比较简单,与普通对象类似,唯一需要注意的就是填写正确的数组类型:
jsonStr = mapper.writeValueAsString(arr);
arr = mapper.readValue(jsonStr, int[].class);
arr = mapper.readValue(jsonStr, String[].class);
arr = mapper.readValue(jsonStr, User[].class);
# 4. 集合的序列化和反序列化
相较于数组,集合的序列化和反序列化就复杂一些,因为泛型的『类型檫除』,Java『分辨』不出 List<String> 和 List<User> ,对 Java 而言它们的类型都是 List.class 。
为了解决的上面的问题,jackson 为我们提供了 TypeReference 来实现对泛型的支持,所以当我们希望使用将以上的数据解析为 List<String>
时需要将 List<String>
『套进』new TypeReference<T
>() { } 中的 T 部分。
这里之所以这么麻烦,其实是为了绕过泛型的类型擦除,来精确地告诉 Jackson 库,我们需要它将 JSON String 转换成何种类型。这是个编程技巧。
List<User> list = mapper.readValue(jsonStr, new TypeReference<List<User>>() { });
所有涉及到有泛型参数的类型转换,都需要用这种方式。
# 5. 处理对象的 NULL 属性
默认情况下,对于对象的值为 NULL 的属性,jackson 默认也是会『包含』在所生成的 JSON 字符串中。
ObjectMapper mapper = new ObjectMapper();
User tom = new User("tom", 21);
String jsonStr = mapper.writeValueAsString(tom);
System.out.println(jsonStr);
// {"name":"tom","age":21,"emailAddress":null}
如果你不希望在所生成的 JSON 格式的字符串中含有值为 NULL 的属性,有两种方案:『注解』和『配置』。
# 方案一:注解
@JsonInclude(Include.NON_NULL)
public class User {
...
}
# 方案二:配置
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
...
# 6. 格式化 Date 类型属性
当所需要序列化和反序列化的对象的属性有 Date 类型时,这里就涉及到 Date 类型的字符串形式的格式问题,为此 @JsonFormat 注解提供了 pattern 属性用以自定义其字符串格式:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthDate;
注意,这种方式对 LocalDatetime 等 Java 8 中的日期时间新特性无效。
# 7. Jackson 对 jsr310 的支持
上面的 @JsonFormat 对于 Java 8 的新的日期时间类(即,jsr310)无效!
Jackson 对 jsr310 的支持在单独的包里:jackson-datatype-jsr310
在引入 jackson-datatype-jsr310 的包之后,我们在创建 Jackson 的 Mapper 对象之后通过注册一个 JavaTimeModule 对象来『告知』Jackson:当遇到 LocalDate、LocalTime 和 LocalDateTime 类型时,以何种方式进行转换。
ObjectMapper mapper = new ObjectMapper();
// 初始化 JavaTimeModule
JavaTimeModule javaTimeModule = new JavaTimeModule();
// 处理 LocalDateTime
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
// 处理 LocalDate
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(dateFormatter));
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(dateFormatter));
// 处理 LocalTime
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(timeFormatter));
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(timeFormatter));
// 注册时间模块, 支持支持 jsr310, 即新的时间类(java.time 包下的时间类)
mapper.registerModule(javaTimeModule);
String jsonStr = mapper.writeValueAsString(...);
# 8. 循环引用
在 Java 中,两个对象可能会互相持有,此时就是循环引用现象。当序列化其中一个对象时,会涉及到它的相关对象,而序列化它的相关对象时,又会再次序列化它自己,而序列化它自己时又需要去序列化它的相关对象 ... ,从而造成一个死循环。
@JsonIgnore 注解用于排除某个属性,这样该属性就不会被 Jackson 序列化和反序列化:
@JsonIgnore
private String emailAddress;
另外,功能相似的还有 @JsonIgnoreProperties 注解,不过它是类注解,可以批量设置:
@JsonIgnoreProperties({"age", "emailAddress"})
public class User {
...
}
在从 JSON 字符串反序列化为 Java 类的时候,@JsonIgnoreProperties(ignoreUnknown=true) 会忽略所有没有 Getter 和 Setter 的属性。该注解在 Java 类和 JSON 不完全匹配的时候很有用。
不过,我们在设计系统时,所追求的对象的关系的目标应该是『有向无环』。所以,尽量从这个根本角度避免对象的相互引用。
# 9. 其它
jackson 支持 JSON 字符串与 Map 对象之间的互转:
ObjectMapper mapper = new ObjectMapper();
Map<String, Object> map = new HashMap<>();
map.put("name", "tom");
map.put("age", 20);
map.put("emailAddress", "[email protected]");
map.put("birthDate", new Date());
String jsonStr = mapper.writeValueAsString(map);
System.out.println(jsonStr);
/****************************************/
Map<String, Object> oth = mapper.readValue(jsonStr, Map.class);
for (Map.Entry<String, Object> entry : oth.entrySet())
System.out.println(entry.getKey() + ", " + entry.getValue());
# 10. 基于 Jackson 的 JsonUtils 工具类
见其它笔记。