Hướng dẫn sử dụng @JsonView trong Jackson

Trong bài viết này, chúng ta sẽ cùng tìm hiểu cách sử dụng @JsonView để serialize/deserialize object và cách customize views và cuối cùng là tích hợp @JsonView với Spring.

Serialize với @JsonView

Phần đầu tiên, chúng ta sẽ sử dụng @JsonView để điều khiển quá trình serialize object.

Tạo Views class đơn giản để sử dụng với @JsonView.

public class Views {
    public static class Public {
    }
}

User class sử dụng xuyên suốt trong bài viết

public class User {
    public int id;

    @JsonView(Views.Public.class)
    public String name;

     public User(int id, String name) {
         this.id = id;
         this.name = name;
     }
 }

Tiếp theo chúng ta sẽ serialize User instance sử dụng @JsonView.

public class Main {

    public static void main(String[] agrs) throws IOException {
        User user = new User(1, "John");

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);

        String result = mapper
                .writerWithView(Views.Public.class)
                .writeValueAsString(user);
        System.out.println(result);

    }
}

Output:

{"name":"John"}

Lưu ý một điểm quan trọng tất cả các field không được đánh dấu @JsonView mặc định sẽ là một phần của View đang sử dụng và được serialize. Để tắt cơ chế này, chúng ta cần vô hiệu hoá option DEFAULT_VIEW_INCLUSION.

mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);

Multiple Views

Trong phần này, chúng ta sẽ phân ra thành nhiều View khác nhau, các field trong Java class sẽ được gom nhóm theo các View tuỳ ý.

Trong phần này, chúng ta sẽ khởi tạo thêm một Internal View thừa kế từu Public View

Note: Các View phải được khai báo theo cơ chế thừa kế, các View con sẽ chứa toàn bộ các field của thuộc tính cha.

public class Views {
    public static class Public {
    }
 
    public static class Internal extends Public {
    }
}

Tạo Item class bao gồm field idname thuộc Public View, và ownerName thuộc Internal View.

public class Item {
    @JsonView(Views.Public.class)
    public int id;
    @JsonView(Views.Public.class)
    public String itemName;
    @JsonView(Views.Internal.class)
    public String owner;

    public Item(int id, String itemName, String owner) {
        this.id = id;
        this.itemName = itemName;
        this.owner = owner;
    }
}

Nếu serialize Item instance trong Public View – chỉ có field idname được serialized sang Json.

public class Main {

    public static void main(String[] agrs) throws IOException {
        Item item = new Item(2, "book", "John");

        ObjectMapper mapper = new ObjectMapper();
        String result = mapper
                .writerWithView(Views.Public.class)
                .writeValueAsString(item);
        System.out.println(result);


    }
}

Output: 

{"id":2,"itemName":"book"}

Nhưng nếu, serialize trong Internal View, tất cả các field trong Item class sẽ được serialized.

public class Main {

    public static void main(String[] agrs) throws IOException {
        Item item = new Item(2, "book", "John");

        ObjectMapper mapper = new ObjectMapper();
        String result = mapper
                .writerWithView(Views.Internal.class)
                .writeValueAsString(item);
        System.out.println(result);
        
    }
}

Output

{"id":2,"itemName":"book","owner":"John"}

Deserialize với @JsonView

Tương tự serialize, deserialize với View nào thì chỉ có các thuộc tính trong View đó mới được nhận giá trị.

Ví dụ khi deserialize User Json trong Public View, chỉ có field name được nhận gía trị.

public class Main {

    public static void main(String[] agrs) throws IOException {
        String json = "{\"id\":1,\"name\":\"John\"}";

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(MapperFeature.DEFAULT_VIEW_INCLUSION);
        User user = mapper
                .readerWithView(Views.Public.class)
                .forType(User.class)
                .readValue(json);

        System.out.println(user.name); // John
        System.out.println(user.id); // 0 - default value

    }
}

Custom Json View

Đôi lúc chúng ta có một số nhu cầu chuyện biệt cần phải custom Json View. Trong phần này, chúng ta sẽ được giới thiệu cách custom Json View qua ví dụ Uppercase field name của User class.

Để làm được, chúng ta sẽ cần đến BeanPropertyWriter BeanSerializerModifier. Trước tiên khởi tạo UpperCasingWriter thừa kế BeanPropertyWriter.

public class UpperCasingWriter extends BeanPropertyWriter {
    BeanPropertyWriter _writer;
 
    public UpperCasingWriter(BeanPropertyWriter w) {
        super(w);
        _writer = w;
    }
 
    @Override
    public void serializeAsField(Object bean, JsonGenerator gen, 
      SerializerProvider prov) throws Exception {
        String value = ((User) bean).name;
        value = (value == null) ? "" : value.toUpperCase();
        gen.writeStringField("name", value);
    }
}

Sau khi có UpperCasingWriter,  khởi tạo MyBeanSerializerModifier sử dụng UpperCasingWriter vừa được tạo ở trên.

public class MyBeanSerializerModifier extends BeanSerializerModifier {

    @Override
    public List<BeanPropertyWriter> changeProperties(
            SerializationConfig config, BeanDescription beanDesc,
            List<BeanPropertyWriter> beanProperties) {
        for (int i = 0; i < beanProperties.size(); i++) {
            BeanPropertyWriter writer = beanProperties.get(i);
            if (writer.getName() == "name") {
                beanProperties.set(i, new UpperCasingWriter(writer));
            }
        }
        return beanProperties;
    }
}

Và cuối cùng deserialize User instance sang Json với kết quả mong muốn là giá trị name sẽ được uppercase.

public class Main {

    public static void main(String[] agrs) throws IOException {
        User user = new User(1, "John");
        SerializerFactory serializerFactory = BeanSerializerFactory.instance
                .withSerializerModifier(new MyBeanSerializerModifier());
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializerFactory(serializerFactory);

        String result = mapper
                .writerWithView(Views.Public.class)
                .writeValueAsString(user);
        System.out.println(result);

    }
}

Output

{"id":1,"name":"JOHN"}

Sử dụng @JsonView với Spring

Phần cuối cùng sẽ đi nhanh cách sử dụng @JsonView với Spring framework. Chúng ta sẽ tận dụng @JsonView để custom response tại API – level.

Ví dụ response Json với Public View

@JsonView(Views.Public.class)
@RequestMapping("/items/{id}")
public Item getItemPublic(@PathVariable int id) {
    return ItemManager.getById(id);
}

Kết quả sẽ là 

{"id":2,"itemName":"book"}

Còn nếu Internal View.

@JsonView(Views.Internal.class)
@RequestMapping("/items/internal/{id}")
public Item getItemInternal(@PathVariable int id) {
    return ItemManager.getById(id);
}

Thì kết quả:

{"id":2,"itemName":"book","ownerName":"John"}

Tóm lược

Như vậy, qua bài viết ngắn này chúng ta đã tìm hiểu cách sử dụng của @JsonView trong Jackson. Nó là một công cụ khá hữu ích để gom nhóm các thuộc tính trong serialize/deserialize theo cơ chế thừa kế.

Nguồn tham khảo

https://www.baeldung.com/jackson-json-view-annotation

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x