Json序列化和反序列化還有這種玩法
Mixin對(duì)于前端開發(fā)者可不陌生,Vue、React等知名前端框架都使用了Mixin。而對(duì)于后端開發(fā),尤其是Java后端開發(fā)來說Mixin卻是一個(gè)很陌生的概念。今天來我們通過Jackson讓后端開發(fā)者也來認(rèn)識(shí)一下Mixin。
場(chǎng)景
比如我們引用了一個(gè)Jar包,其中的某個(gè)類在某個(gè)場(chǎng)景需要反序列化,但是這個(gè)類沒有提供默認(rèn)構(gòu)造。咋辦呢?把原來的項(xiàng)目拉下來,重寫一下?下下策! 你可以使用Jackson提供的Mixin特性來解決這個(gè)問題。
Jackson中的Mixin
Jackson中的Mixin(混入)我們可以這樣解讀它:將目標(biāo)對(duì)象無法實(shí)現(xiàn)的序列化或反序列化功能通過一個(gè)混入對(duì)象進(jìn)行配置,在序列化或反序列化的時(shí)候把這些個(gè)性化配置混入到目標(biāo)對(duì)象中。混入不改變目標(biāo)對(duì)象本身的任何特性,混入對(duì)象和目標(biāo)對(duì)象是映射的關(guān)系。接下來我們來實(shí)現(xiàn)一個(gè)混入的DEMO。
Mixin的實(shí)現(xiàn)
我們有一個(gè)User類,為了演示需要,我們極端一些,實(shí)際開發(fā)中不太會(huì)出現(xiàn)這種極端情況。這個(gè)User沒有無參構(gòu)造,也沒有屬性的getter方法。
- public class User {
- private final String name;
- private final Integer age;
- public User(String name, Integer age) {
- this.name = name;
- this.age = age;
- }
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- }
編寫Mixin類
我想對(duì)這個(gè)極端的User進(jìn)行序列化和反序列化。按以前的玩法我們?cè)赨ser類上加上@JsonAutoDetect注解就可以實(shí)現(xiàn)序列化了;加上@JsonDeserialize注解并指定反序列化類就可以反序列化了。不過今天我們不需要對(duì)User進(jìn)行任何更改,只需要編寫一個(gè)Mixin類把上述兩個(gè)注解配置好就可以了。
- @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE,
- isGetterVisibility = JsonAutoDetect.Visibility.NONE)
- @JsonIgnoreProperties(ignoreUnknown = true)
- @JsonDeserialize(using = UserMixin.UserDeserializer.class)
- public abstract class UserMixin {
- /**
- * 反序列化類
- **/
- static class UserDeserializer extends JsonDeserializer<User> {
- @Override
- public User deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
- ObjectMapper mapper = (ObjectMapper) p.getCodec();
- JsonNode jsonNode = mapper.readTree(p);
- String name = readJsonNode(jsonNode, "name").asText(null);
- String age = readJsonNode(jsonNode, "age").asText(null);
- Integer ageVal = Objects.isNull(age)? null: Integer.valueOf(age);
- return new User(name,ageVal);
- }
- private JsonNode readJsonNode(JsonNode jsonNode, String field) {
- return jsonNode.has(field) ? jsonNode.get(field) : MissingNode.getInstance();
- }
- }
- }
其它注解可以參考往期的Jackson文章的介紹
Mixin映射目標(biāo)類
編寫完Mixin類后,我們通過ObjectMapper中的addMixIn方法把UserMixin和User映射起來。并編寫一個(gè)序列化和反序列化的例子。
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.addMixIn(User.class, UserMixin.class);
- User felord = new User("felord", 12);
- String json = objectMapper.writeValueAsString(felord);
- //{"name":"felord","age":12}
- System.out.println("json = " + json);
- String jsonStr = "{\"name\":\"felord\",\"age\":12}";
- User user = objectMapper.readValue(jsonStr, User.class);
- // User{name='felord', age=12}
- System.out.println("user = " + user);
這樣我們?cè)诓粚?duì)目標(biāo)類進(jìn)行任何改變的情況下實(shí)現(xiàn)了個(gè)性化的JSON序列化和反序列化。
Jackson中的Module
Jackson還提供了模塊化功能,可以將個(gè)性化配置進(jìn)行模塊化統(tǒng)一管理,而且可以按需引用,甚至可插拔。它同樣能夠管理一組Mixin。聲明一個(gè)Jackson Module非常簡(jiǎn)單,繼承SimpleModule覆寫它的一些方法即可。針對(duì)Mixin我們可以這樣寫:
- public class UserModule extends SimpleModule {
- public UserModule() {
- super(UserModule.class.getName());
- }
- @Override
- public void setupModule(SetupContext context) {
- context.setMixInAnnotations(User.class,UserMixin.class);
- }
- }
Module同樣可以注冊(cè)到ObjectMapper中,同樣也能實(shí)現(xiàn)我們想要的效果:
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.registerModule(new UserModule());
- // 省略
Module的功能更加強(qiáng)大。平常我們會(huì)使用以下幾個(gè)Module:
- jackson-module-parameter-names 此模塊能夠訪問構(gòu)造函數(shù)和方法參數(shù)的名稱
- jackson-datatype-jdk8 除了Java8的時(shí)間API外其它新特性的的支持
- jackson-datatype-jsr310 用以支持Java8新增的JSR310時(shí)間API
另外Spring Security也提供了Module支持SecurityJackson2Modules,它包含了下面的一些模塊:
- ObjectMapper mapper = new ObjectMapper();
- mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
- mapper.registerModule(new CoreJackson2Module());
- mapper.registerModule(new CasJackson2Module());
- mapper.registerModule(new WebJackson2Module());
- mapper.registerModule(new WebServletJackson2Module());
- mapper.registerModule(new WebServerJackson2Module());
- mapper.registerModule(new OAuth2ClientJackson2Module());
建議看一下SecurityJackson2Modules源碼,研究并模仿一下Module的使用。