添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Jackson 使用指南

2021-08-10, 星期二, 22:25

Java Dev
  • FasterXML/jackson-annotations
  • FasterXML/jackson-databind
  • Jackson Annotation Examples
  • Jackson 的核心由三个模块组成

  • Streaming( jackson-core )提供流式解析工具 JsonParser ,流式生成工具 JsonGenerator
  • Annotations( jackson-annotations )提供注解
  • Databind ( jackson-databind ) 提供 ObjectMapper 用于序列化和反序列化,依赖前两个模块
  • 强大灵活的注解和 API
  • 通过 Module 模块扩展可支持 XML、YAML、Properties
  • org.springframework.boot:spring-boot-starter-web 默认集成 Jackson,基于 Spring Boot 的 Web 项目可直接使用
  • ObjectMapper mapper = new ObjectMapper();
    User randomUser = new User();
    List<User> users = new ArrayList<>() {{
        add(randomUser());
    mapper.writeValueAsString(user);
    mapper.writeValueAsString(users);
    
    // 反序列化为对象
    Foo foo = mapper.readValue(objectStr, Foo.class);
    // 反序列化为数组
    Foo[] foos = mapper.readValue(arrayStr, Foo[].class);
    // 反序列化为基本类型的 List
    List<Integer> list = mapper.readValue(arrayStr, List.class);
    // 反序列化为复杂类型的 List
    List<Foo> list = mapper.readValue(arrayStr, new TypeReference<List<Foo>>() {
    // 反序列化为复杂类型的 Map
    Map<String, Foo> map = mapper.readValue(objectStr, new TypeReference<Map<String, Foo>>() {
    

    TreeModel

    如果业务逻辑只需要 JSON 中的一部分字段,或者 JSON 的结构需要在运行时推导,可用 TreeModel 转换为 JsonNode 来处理。

    String objectStr = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }";
    JsonNode node = objectMapper.readTree(json);
    String color = node.get("color").asText();
    // Output: color -> Black
    

    在 Java 中直接创建 JSON 和 JSONArray 也需要使用 TreeModel

    JsonNode node = mapper.valueToTree(fromValue);
    // or
    JsonNode node = mapper.convertValue(fromValue, JsonNode.class);
    // 使用 ObjectMapper
    ObjectNode node = mapper.createObjectNode();
    ArrayNode nodes = mapper.createArrayNode();
    

    由于值/对象/数组都被视为 JsonNode,可以使用 isValueNodeisArrayisObject 等方法判断

    if (column.isArray()) {
    	for (final JsonNode field : column) {
    		JsonNode prop = field.get("prop");
    		if (prop.isValueNode()) {
    			logger.info("field {} has loaded.", prop.asText());
    

    path 方法可用于按已知路径寻找指定节点

    JsonNode located = root.path("name").path("last");
    

    ObjectMapper 常用配置

    Spring Boot 项目支持在 application.* 文件的 spring.jackson 路径下配置 ObjectMapper 的部分行为,也可以在代码中使用 configure(SerializationFeature f, boolean state)enable/disable(SerializationFeature f) 配置。

    // 序列化
    // 空对象不抛出异常
    mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
    // 反序列化
    // 允许 JSON 中存在 Bean 未定义的属性
    mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
    // 允许基本类型字段为空
    mapper.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES)
    // 空字符串按 null 处理
    mapper.enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
    // 允许枚举序列化为数字
    mapper.disable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS)
    

    registerModule

    java.time API

    使用 Java 8 提供的 java.time API 类型(例如 java.time.LocalDateTimejava.time.OffsetDateTime)时,需要添加 com.fasterxml.jackson.datatype:jackson-datatype-jsr310 依赖并在初始化 ObjectMapper 时注册 JavaTimeModule

    mapper.registerModule(new JavaTimeModule());
    

    默认配置下 java.time.OffsetDateTime 会被序列化为一个 Unix Timestamp 如1653448397.096875000java.time.LocalDateTime 则会被序列化为一个数组如 [2022,5,25,11,13,17,96975000]。要取消这一行为(和 Spring MVC 一样处理为 ISO 8601 字符串),使用 .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)

    时间对象的序列化与反序列化参考自定义序列化和反序列化部分。

    Optional<T>

    类成员是 Optional<T> 时,序列化会获得一个 {"present":true},而反序列化会抛出 JsonMappingException 异常。

    使用 com.fasterxml.jackson.datatype:jackson-datatype-jdk8 并注册 Jdk8Module,一个空的 Optional<T> 会被映射为 null,而反序列化能够正确地填充属性。

    objectMapper.registerModule(new Jdk8Module());
    

    同样支持 OptionalLongOptionalDouble

    @JsonProperty

    修饰成员变量和方法,可在 JSON 字段名与类属性名称不一致时指定 JSON 字段的名称。index 可以指定属性在序列化结果中的顺序。

    @JsonProperty 修饰的 private 属性即使没有 getter 和 setter 也能被成功序列化和反序列化。

    打包处理未知字段

    @JsonAnyGetter@JsonAnySetter 可以把未知或动态变化的字段通过 Map 序列化和反序列化。

    public class ChangeableUser {
        private long id;
        private Map<String, String> extendsProperties = new HashMap<>();
        @JsonAnyGetter
        public Map<String, String> getExtendsProperties() {
            return extendsProperties;
        @JsonAnySetter
        public void addExtendsProperty(String key, String value) {
            extendsProperties.put(key, value);
    

    反序列化形如 {"name":"John Doe","age":24,"phone":"123456789","sex":"male","id":2301} 的 JSON 串后,可以通过 extendsProperties 访问未定义的属性。

    指定构造函数

    Jackson 在反序列化时会选择无参构造函数或 @JsonCreator 修饰的构造函数,这一点在需要做映射操作时比较有用(例如从 code 映射到枚举)。

    @JsonValue@JsonCreator 可用于枚举的序列化和反序列化。下方示例告诉 Jackson 在序列化结果中使用 code,在反序列化过程中映射回枚举。

    public enum FruitEnum {
        Banana(0, "ba"),
        Apple(1, "ap");
        final int code;
        final String name;
        FruitEnum(int code, String name) {
            this.code = code;
            this.name = name;
        @JsonValue
        public int getCode() {
            return code;
        @JsonCreator
        public static FruitEnum getFruit(int code) {
            for (FruitEnum fruitEnum : FruitEnum.values()) {
                if (fruitEnum.getCode() == code) {
                    return fruitEnum;
            return null;
    

    @JsonIgnoreProperties 接受一个属性名称的集合,用于屏蔽 Bean 中的属性,使其不会被添加到序列化结果中,也可以使用 @JsonIgnore 注解单独设置每个属性。

    @JsonIgnoreProperties({ "id", "password" })
    public class SystemUser {
        private long id;
        private String name;
        private String password;
        @JsonIgnore
        private String address;
    

    @JsonIgnoreType 修饰的类作为其他类的成员时,无论是序列化还是反序列化都会被忽略。

    @JsonAutoDetect 注解可以根据 fieldVisibility 的值和属性是否为 public/protected/private 判断是否要在序列化/反序列化过程中处理。

    自定义序列化和反序列化

    一些复杂对象在序列化和反序列化过程中需要编写一些逻辑代码,例如:

  • 逗号分割的字符串与 List<String> 转换
  • ISO8601 格式的日期时间字符串与 OffsetDateTime/LocalDateTime 的转换,后者需要额外处理时区信息,或改为使用 DateTimeFormatter.ISO_LOCAL_DATE_TIME
  • 自定义的序列化需要继承 StdSerializer<T> 实现 serialize 方法。

    @Override
    public void serialize(OffsetDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeString(value.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
    

    使用 @JsonSerialize(using = CustomSerializer.class) 修饰属性。

    自定义的反序列化操作需要继承 StdDeserializer<T> 实现 deserialize 方法。

    @Override
    public LocalDateTime deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        return LocalDateTime.parse(jsonParser.getText(), DateTimeFormatter.ISO_OFFSET_DATE_TIME);
    

    使用 @JsonDeserialize(using = CustomDeserializer.class) 修饰属性。

    @JsonRootName

    public record Cargo(String name, BigDecimal weight) {}
    // 默认的序列化结果
    {"name":"banana","weight":1}
    

    @JsonRootName 注解可使用类名或 value 值作为键值包裹这个结果。

    @JsonRootName("_cargo")
    public record Cargo(String name, BigDecimal weight) {}
    // get
    {"_cargo":{"name":"banana","weight":1}}
    

    需要 Mapper 开启 (UN)WRAP_ROOT_VALUE 特性。

    mapper.enable(SerializationFeature.WRAP_ROOT_VALUE);
    mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
    

    @JsonUnwrapped

    @JsonRootName 相反,该注解修饰的属性类可以被 flattened

    public class UnwrappedUser {
        public int id;
        @JsonUnwrapped
        public Name name;
        public static class Name {
            public String firstName;
            public String lastName;
    // get
    {"id":1,"firstName":"John","lastName":"Doe"}
    

    @JsonRawValue

    在序列化过程中按原始值输出。

    public class RawEntity {
        @JsonRawValue
        private String raw = "{\"name\":\"John Doe\"}";
    // get
    {"raw":{"name":"John Doe"}}
    
    public class Zoo {
        public Animal animal;
        @JsonTypeInfo(
          use = JsonTypeInfo.Id.NAME,
          include = As.PROPERTY,
          property = "type")
        @JsonSubTypes({
            @JsonSubTypes.Type(value = Dog.class, name = "dog"),
            @JsonSubTypes.Type(value = Cat.class, name = "cat")
        public static class Animal {
            public String name;
        @JsonTypeName("dog")
        public static class Dog extends Animal {
            public double barkVolume;
        @JsonTypeName("cat")
        public static class Cat extends Animal {
            boolean likesCream;
            public int lives;
    

    建造者模式

    建造者模式可以用来一步步构造复杂的对象,此时对象的序列化和反序列化需要配合使用 @JsonPOJOBuilder@JsonDeserialize

    @JsonDeserialize(builder = Person.Builder.class)
    public class Person {
        private final String name;
        private final Integer age;
        private Person(String name, Integer age) {
            this.name = name;
            this.age = age;
        @JsonPOJOBuilder
        static class Builder {
            String name;
            Integer age;
            Builder withName(String name) {
                this.name = name;
                return this;
            Builder withAge(Integer age) {
                this.age = age;
                return this;
            public Person build() {
                return new Person(name, age);
    

    如果 Builder 中没有使用 withXXXbuild 方法赋值和构建对象,需要为 @JsonPOJOBuilde 注解指明 buildMethodNamewithPrefix

    @JsonPOJOBuilder(buildMethodName = "create", withPrefix = "set")
    static class Builder {
        String name;
        Integer age;
        Builder setName(String name) {
            this.name = name;
            return this;
        Builder setAge(Integer age) {
            this.age = age;
            return this;