Annotation @JsonView cho phép **dynamic include/exclude field** khi serialize/deserialize dựa trên **View class**. Cấu hình qua ObjectMapper.writerWithView().
Cú pháp
@Target({ANNOTATION_TYPE, METHOD, FIELD})
@Retention(RUNTIME)
public @interface JsonView {
Class<?>[] value() default {};
}
Ví dụ: Public vs Internal View
import com.fasterxml.jackson.annotation.*;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JsonViewTest {
public static void main(String[] args) throws Exception {
ObjectMapper mapper = new ObjectMapper();
// Public view - chỉ field có @JsonView(Public.class)
String publicJson = mapper.writerWithView(Views.Public.class)
.writeValueAsString(new Person());
// Internal view - field Public + Internal (Internal extends Public)
String internalJson = mapper.writerWithView(Views.Internal.class)
.writeValueAsString(new Person());
System.out.println("Public: " + publicJson);
System.out.println("Internal: " + internalJson);
}
}
class Person {
@JsonView(Views.Public.class)
public long personId = 115;
@JsonView(Views.Public.class)
public String personName = "Raja Ramesh";
@JsonView(Views.Internal.class)
public String gender = "male";
}
class Views {
static class Public {}
static class Internal extends Public {} // Internal kế thừa Public → thấy cả 2
}
Output
Public: {"personId":115,"personName":"Raja Ramesh"}
Internal: {"personId":115,"personName":"Raja Ramesh","gender":"male"}
Cơ chế kế thừa View
- View class có thể **extends** view khác.
- Sub-view **kế thừa** field của parent view.
- Ví dụ:
Internal extends Public→ Internal thấy field Public + Internal.
Deserialize với View
// Chỉ deserialize field thuộc view Public
Person p = mapper.readerWithView(Views.Public.class)
.forType(Person.class)
.readValue(jsonString);
Use case thực tế
- **API versioning**: v1 = Public view, v2 = Extended view.
- **Role-based**: User view vs Admin view (Internal extends Public).
- **Performance**: Tránh serialize field nặng (blob, large text) cho list view.
- **DTO replacement**: Thay vì tạo nhiều DTO class, dùng 1 entity + multiple views.
Best Practice
- Đặt View class là
static inner classcủa entity hoặc package riêng. - Dùng interface thay vì class cho view (Jackson hỗ trợ cả hai).
- Kết hợp với
@JsonIgnorecho field không bao giờ serialize. - Disable
MapperFeature.DEFAULT_VIEW_INCLUSIONđể field không có @JsonView bị exclude mặc định.