jackson根據屬性名動態序列化物件欄位

weixin_33924312發表於2019-01-23

在info上有篇文章講的是 《為什麼說 GraphQL 可以取代 REST API?》,裡面提到這樣一段:


9676606-a5bceba06848a39b.png
圖片.png

如果我們要實現這種方式:GET /users/?fields=name,address&include=resumes,subscriptions.按須只返回相應的欄位值。
有幾個點要考慮,今天我們只說其中的一個東西,這個問題就是要根據指定屬性名來序列化物件的值。傳統的方法都是通過 annotations來實現,但是這種方式使用方式很死,有沒有一種方法可以不通過annotations來完成呢,那就是通過jackson的@JsonFilter和addMixIn()方法配合使用。下面我們一步步來完成這個功能。
第一步:我們須要定義一個實體類 User,它就是我們須要序列化的物件:

public class User {
    private String firstName;
    
    private String lastName;

    private String name;

    private Integer age;

    //getter setter
}

第二步:我們來建立一個通用的@JsonFilter物件,注意這個類沒有任何屬性:

@JsonFilter("userFilter")
public class UserFilter {
}

第三步:通過ObjectMapper預設提供的FilterProvider的實現類SimpleFilterProvider來配置相關的資訊,並進行輸出結果:

public class AppendTest {

    public static void main(String[] args) {
        User user = new User();
        user.setFirstName("water");
        user.setLastName("lang");
        user.setName("water lang");
        user.setAge(30);

        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.addMixIn(
                Object.class, UserFilter.class);

        FilterProvider filters = new SimpleFilterProvider()
                .addFilter("userFilter",
                        SimpleBeanPropertyFilter.serializeAllExcept(
                                "name"));
        ObjectWriter writer = objectMapper.writer(filters);

        try {
            System.out.println(writer.writeValueAsString(user));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

我們可以看到輸出的結果為:
{"firstName":"water","lastName":"lang","age":30}
明顯可以找到這個name這個欄位沒有被序列化出來。

如果我們要繼續讓這個序列化的欄位進行動態改變怎麼做呢?很簡單,我們只須要再建立一個filterProvider就行了,然後通過writer()方法配置進去就行了。

package com.github.bohnman.squiggly.examples.springboot.test;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.github.bohnman.squiggly.examples.springboot.model.User;

public class AppendTest {

    public static void main(String[] args) {
        User user = new User();
        user.setFirstName("water");
        user.setLastName("lang");
        user.setName("water lang");
        user.setAge(30);

        ObjectMapper objectMapper = new ObjectMapper();

        objectMapper.addMixIn(
                Object.class, UserFilter.class);

        FilterProvider filters = new SimpleFilterProvider()
                .addFilter("userFilter",
                        SimpleBeanPropertyFilter.serializeAllExcept(
                                "name"));
        ObjectWriter writer = objectMapper.writer(filters);

        try {
            System.out.println(writer.writeValueAsString(user));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

        FilterProvider ageFilter = new SimpleFilterProvider()
                .addFilter("userFilter",
                        SimpleBeanPropertyFilter.serializeAllExcept(
                                "age","lastName"));
        ObjectWriter writer1 =   objectMapper.writer(ageFilter);
        try {
            System.out.println(writer1.writeValueAsString(user));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }
}

輸出結果:
{"firstName":"water","lastName":"lang","age":30}
{"firstName":"water","name":"water lang"}

注意的是我們這裡沒有直接使用ObjectMapper來做而是使用的ObjectWriter類來實現。當jackson在2.x版本,官方推薦優先使用後者。還有一個就是ObjectMapper具體應該是單例還是物件池(比如apache的ObjectPool)還是每次建立一個在github有很多爭議。如果併發量不是很大的情況下可以使用單例,在併發量很大的情況下推薦使用物件池來實現。
https://stackoverflow.com/questions/3907929/should-i-declare-jacksons-objectmapper-as-a-static-field

相關文章