前言

平日里项目中处理JSON一般用的都是阿里巴巴的fastjson,现在发现使用SpringBoot内置的Jackson的序列化和反序列化也挺方便的。Jackson不但可以完成简单的序列化和反序列化操作,也能实现复杂的个性化的序列化和反序列化操作。

——以上文案皆盗于鸟叔博客,勿怪勿怪。

准备

实体类User

public class User implements Serializable {
    private static final long serialVersionUID = -3180230416244251692L;

    private Integer id;
    private String name;
    private Date birth;

    // NoArgsConstructor
    // AllArgsConstructor
    // Getter and Setter
    // ToString
}

ObjectMapper API

序列化与反序列化

序列化属性SerializationFeature

WRAP_ROOT_VALUE(false),                   
INDENT_OUTPUT(false),
FAIL_ON_EMPTY_BEANS(true),
FAIL_ON_SELF_REFERENCES(true),
WRAP_EXCEPTIONS(true),
FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS(true),
CLOSE_CLOSEABLE(false),
FLUSH_AFTER_WRITE_VALUE(true),
WRITE_DATES_AS_TIMESTAMPS(true),
WRITE_DATE_KEYS_AS_TIMESTAMPS(false),
WRITE_DATES_WITH_ZONE_ID(false),
WRITE_DURATIONS_AS_TIMESTAMPS(true),
WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS(false),
WRITE_ENUMS_USING_TO_STRING(false),
WRITE_ENUMS_USING_INDEX(false),
WRITE_ENUM_KEYS_USING_INDEX(false),
WRITE_SINGLE_ELEM_ARRAYS_UNWRAPPED(false),
WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS(true),
ORDER_MAP_ENTRIES_BY_KEYS(false),
EAGER_SERIALIZER_FETCH(true),
USE_EQUALITY_FOR_OBJECT_ID(false);

反序列化属性DeserializationFeature

USE_BIG_DECIMAL_FOR_FLOATS(false),
USE_BIG_INTEGER_FOR_INTS(false),
USE_LONG_FOR_INTS(false),
USE_JAVA_ARRAY_FOR_JSON_ARRAY(false),
FAIL_ON_UNKNOWN_PROPERTIES(true),
FAIL_ON_NULL_FOR_PRIMITIVES(false),
FAIL_ON_NUMBERS_FOR_ENUMS(false),
FAIL_ON_INVALID_SUBTYPE(true),
FAIL_ON_READING_DUP_TREE_KEY(false),
FAIL_ON_IGNORED_PROPERTIES(false),
FAIL_ON_UNRESOLVED_OBJECT_IDS(true),
FAIL_ON_MISSING_CREATOR_PROPERTIES(false),
FAIL_ON_NULL_CREATOR_PROPERTIES(false),
FAIL_ON_MISSING_EXTERNAL_TYPE_ID_PROPERTY(true),
FAIL_ON_TRAILING_TOKENS(false),
WRAP_EXCEPTIONS(true),
ACCEPT_SINGLE_VALUE_AS_ARRAY(false),
UNWRAP_SINGLE_VALUE_ARRAYS(false),
UNWRAP_ROOT_VALUE(false),
ACCEPT_EMPTY_STRING_AS_NULL_OBJECT(false),
ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT(false),
ACCEPT_FLOAT_AS_INT(true),
READ_ENUMS_USING_TO_STRING(false),
READ_UNKNOWN_ENUM_VALUES_AS_NULL(false),
READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE(false),
READ_DATE_TIMESTAMPS_AS_NANOSECONDS(true),
ADJUST_DATES_TO_CONTEXT_TIME_ZONE(true),
EAGER_DESERIALIZER_FETCH(true);

environment配置

可配置

  • spring.jackson.deserialization.=true|false
  • spring.jackson.generator.=true|false
  • spring.jackson.mapper.=true|false
  • spring.jackson.parser.=true|false
  • spring.jackson.serialization.=true|false
  • spring.jackson.serialization-inclusion=always|non_null|non_absent|non_default|non_empty

application.yml:

spring:
  jackson:
    # 日期格式化
    date-format: yyyy-MM-dd HH:mm:ss
    # 设置空属性何如序列化
    default-property-inclusion: non_empty
    # 序列化
    serialization:
    # 反序列化
    deserialization:
    # 解析    
    parser: 

configuration配置

在@Configuration类中生成bean:

@Configuration
public class JacksonConfig {

    @Bean
    @Primary
    @ConditionalOnMissingBean(ObjectMapper.class)
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // 序列化日期格式
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
        // 没有匹配的属性名称时不作处理
        objectMapper.configure(MapperFeature.AUTO_DETECT_FIELDS, true);

        // 序列化
        //禁止序列化空值
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
        objectMapper.configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
        objectMapper.configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, true);
        // 不包含空值属性
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);

        // 反序列化
        //禁止遇到空原始类型时抛出异常,用默认值代替
        objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
        objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
        // 禁止遇到未知(新)属性时报错,支持兼容扩展
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT, true);
        objectMapper.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
        objectMapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, true);
        objectMapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true);
        objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);
        objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true);

        return objectMapper;
    }
}

Rest接口:

@RestController
@RequestMapping("/user")
public class UserController {
    private static Logger log = LoggerFactory.getLogger(UserController.class);

    @Autowired
    private ObjectMapper objectMapper;

    // 序列化接口
    @GetMapping("/get")
    public String getUser() throws JsonProcessingException {
        User user = new User(1, "KHighness", new Date());
        return objectMapper.writeValueAsString(user);
    }

    // 反序列化接口
    @PostMapping("/save")
    public void saveUser(@RequestBody String userJsonList) throws IOException {
        List<User> userList = objectMapper.readValue(userJsonList, new TypeReference<List<User>>() {});
        userList.forEach(e -> {log.info(e.toString());});
    }
}

CURL测试(windows下建议使用cmd进行测试,用powershell会到导致POST错误)
序列化测试:

$ curl -X GET http://localhost:3333/user/get

序列化测试结果:

{"id":1,"name":"KHighness","birth":"2021-04-15"}

反序列化测试:

$ curl -H "Content-Type:application/json" -X POST --data "[{\"id\":1, \"name\":\"Khighness\", \"birth\":\"2001-09-11\"}, {\"id\":2, \"name\":\"FlowerK\", \"birth\":\"2003-07-24\"}]"  http://localhost:3333/user/save

反序列化测试结果:

[{"id":1,"name":"Khighness","birth":"2001-09-11"},{"id":2,"name":"FlowerK","birth":"2003-07-24"}]

Jackson注解

(1)@JsonProperty
作用在属性上,用于序列化和反序列化时为JSON key指定一个别名。
例如:

@JsonProperty("bth")
private Date birth;

此时序列化测试结果为:

{"id":1,"name":"KHighness","bth":"2021-04-16"}

(2)@JsonIgnore
作用在属性上,用于在序列化和反序列化时忽略此属性。
例如:

@JsonIgnore
private String name;

此时序列化测试结果为:

{"id":1,"birth":"2021-04-16"}

(3)@JsonIgnoreProperties
作用在类上,用于忽略一组属性。
例如:

@JsonIgnoreProperties({"id", "birth"})

此时序列化测试结果为:

{"name":"KHighness"}

(4)@JsonFormat
作用在日期属性上,用于格式化。
例如:

@JsonFormat(pattern = "yyyy.MM.dd")
private Date birth;

此时序列化测试结果为:

{"id":1,"userName":"KHighness","birth":"2021.04.16"}

(5)@JsonNaming
作用在类上,用于指定一个命名策略。

Jackson自带了五种(两种)命名策略,使用方式,

@JsonNaming(PropertyNamingStrategy.<Strategy>.class)
public class User {
     private String userName;   
}
命名策略 中文描述 作用结果
KebabCaseStrategy 中划线 user-name
SnakeCaseStrategy 下划线 user_name
UpperCamelCaseStrategy 大驼峰 UserName
LowerCaseStrategy 全小写 username
LowerDotCaseStrategy 小写点 user.name

(6)@JsonSerialize
作用在类上,指定一个类来自定义序列化,该类必须实现JsonSerializer接口。
例如:

public class UserSerializer extends JsonSerializer<User> {
    @Override
    public void serialize(User user, JsonGenerator jsonGenerator, 
                          SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeStringField("USER-NAME", user.getUserName());
        jsonGenerator.writeEndObject();
    }
}
@JsonSerialize(using = UserSerializer.class)
public class User implements Serializable {
    private static final long serialVersionUID = -3180230416244251692L;
    private String userName;
    // ...
}

此时序列化接口测试结果为:

{"USER-NAME":"KHighness"}

(7)@JsonDeserialize
作用在类上,指定一个类来自定义反序列化,该类必须实现JsonDeserializer接口。
例如:

public class UserDeserializer extends JsonDeserializer<User> {
    @Override
    public User deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
            throws IOException, JsonProcessingException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        String userName = node.get("user-name").asText();
        User user = new User();
        user.setUserName(userName);
        return user;
    }
}
@JsonDeserialize(using = UserDeserializer.class)
public class User implements Serializable {
    private static final long serialVersionUID = -3180230416244251692L;
    private String userName;
    // ...
}

反序列化测试:

curl -H "Content-Type:application/json" -X POST --data "[{\"user-name\":\"Khighness\"}, {\"user-name\":\"FlowerK\"}]"  http://localhost:3333/user/save
[{"userName":"Khighness"},{"userName":"FlowerK"}]

反序列化测试结果为:

[{"userName":"Khighness"},{"userName":"FlowerK"}]

(8)@JsonView
作用在类、属性和方法上,用来序列化组。
比如对于User对象,某些情况下只返回userName即可,

而某些情况下需要返回全部属性。因此User对象可以这样定义:

public class User implements Serializable {
    private static final long serialVersionUID = -3180230416244251692L;

    // 仅包含userName
    public interface UserNameView {};

    // 包含全部属性
    public interface AllUserFieldView extends UserNameView {};

    @JsonView(AllUserFieldView.class)
    private Integer id;

    @JsonView(UserNameView.class)
    private String userName;

    @JsonView(AllUserFieldView.class)
    private Date birth;

    // ...
}

然后在controller的方法上使用@JsonView,可以指定序列化组名。
使用组名UserNameView:

@JsonView(User.UserNameView.class)
@GetMapping("/get")
public User getUser() throws JsonProcessingException {
    User user = new User(1, "KHighness", new Date());
    return user;
}

序列化测试结果为:

{"userName":"KHighness"}

当组名指定为AllUserFieldView时,序列化测试结果为:

{"id":1,"userName":"KHighness","birth":"2021-04-17"}

参考: