Hướng dẫn xử lý Date trong Json – Jackson

Trong bài viết này chúng ta sẽ cùng nhau tìm cách xử lý Date trong Java khi serialize/deserialize sang Json với Jackson.

Maven dependency

Để sử dụng Jackson trong Project Maven chúng ta cần thêm dependency sau:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.9.8</version>
</dependency>

Ở đây, mình sử dụng phiên bản 2.9.8, các bạn có thể sử dụng phiên bản mới hơn tại maven central.

Configuration

Trước tiên, chúng ta sẽ tạo Event class sử dụng xuyên suốt trong bài viết.

public class Event {

    private String name;
    private Date eventDate;

    public Event() {
    }

    public Event(String name, Date eventDate) {
        this.name = name;
        this.eventDate = eventDate;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Date getEventDate() {
        return eventDate;
    }

    public void setEventDate(Date eventDate) {
        this.eventDate = eventDate;
    }
}

Serialize Date sang Timestamp

Phần đầu tiên sẽ hướng dẫn cách serialize java.util.Date với Jackson. 

Ví dụ serialize Event object sang Json.

public class Main {

    public static void main(String[] agrs) throws IOException, ParseException {

        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
        df.setTimeZone(TimeZone.getTimeZone("UTC"));

        Date date = df.parse("01-01-1970 01:00");
        Event event = new Event("party", date);

        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(event);
        System.out.println(json);
    }
}

Một điểm quan trọng chúng ta cần lưu ý rằng Jackson mặc định sẽ chuyển Date sang Timestamp (số millisecond tính từ 01 – 01 – 1970 UTC) khi serialize sang Json. 

Như vậy, kết quả thực tế là: 

{
   "name":"party",
   "eventDate":3600000
}

Serialize Date theo chuẩn ISO – 8601

Như đã đề cập ở trên, mặc định Jackson sẽ chuyển Date sang định dạng timestamps khi serialize. Để serialize Date sang Json theo chuẩn IOS – 8601 

public class Main {

    public static void main(String[] agrs) throws IOException, ParseException {

        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm");
        df.setTimeZone(TimeZone.getTimeZone("UTC"));

        String toParse = "01-01-1970 02:30";
        Date date = df.parse(toParse);
        Event event = new Event("party", date);

        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // StdDateFormat is ISO8601 since jackson 2.9
        mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true));
        String result = mapper.writeValueAsString(event);
        System.out.println(result);

    }
}

Kết quả serialize là định dạng Date theo chuẩn ISO cho khả năng đọc hiểu Json dễ dàng hơn timestamps.

{
     "name":"party",
     "eventDate":"1970-01-01T02:30:00.000+00:00"
}

Configure ObjectMapper DateFormat

Giải pháp ở phần trước cho phép chúng ta định dạng theo chuẩn ISO, thế nhưng vẫn còn rất nhiều định dạng Date khác mà từng dự án riêng sẽ cần.

Trong phần này chúng ta sẽ cấu hình định dạng cho ObjectMapper cho phép chúng ta chỉ định bất kỳ định dạng Date nào có sẵn.

Ví dụ định dạng dd-MM-yyyy cho Date khi serialize sang Json.

SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy");
ObjectMapper mapper = new ObjectMapper();
        mapper.setDateFormat(df);
public class Main {

    public static void main(String[] agrs) throws IOException, ParseException {

        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy");

        String toParse = "20-12-2014 02:30";
        Date date = df.parse(toParse);
        Event event = new Event("party", date);

        ObjectMapper mapper = new ObjectMapper();
        mapper.setDateFormat(df);

        String result = mapper.writeValueAsString(event);
        System.out.println(result);
        
    }
}

Output: 

{
    "name":"party",
    "eventDate":"20-12-2014"
}

Sử dụng @JsonFormat để định dạng Date

Với cách cấu hình trên thì tất cả các Object chứa Date field đều được định dạng theo cấu hình mà chúng ta đã cài đặt sẵn. Để kiểm soát định dạng Date cho từng class riêng biệt chúng ta có thể sử dụng @JsonFormat cho các Date field tương ứng.

public class Event {
    public String name;
 
    @JsonFormat
      (shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
    public Date eventDate;
    // ... 
}

Tiếp theo thử bỏ cấu hình định dạng Date cho ObjectMapper và kiểm thử.

public class Main {

    public static void main(String[] agrs) throws IOException, ParseException {

        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
        df.setTimeZone(TimeZone.getTimeZone("UTC"));

        String toParse = "20-12-2014 02:30:00";
        Date date = df.parse(toParse);
        Event event = new Event("party", date);

        ObjectMapper mapper = new ObjectMapper();
        String result = mapper.writeValueAsString(event);
        System.out.println(result);

    }
}

Output:

{
     "name":"party",
     "eventDate":"19-12-2014"
}

Cusom Date Serialize

Nếu có những yêu cầu đặc biệt mà các định dạng Date hiện hành đều không đáp ứng được, thì chúng ta có thể custom serializer với @JsonSerialize annotation.

Giả sử mình chỉ muốn xuất định đạng Date sang Json dd/MM

public class CustomDateSerializer extends StdSerializer<Date> {

    private SimpleDateFormat formatter
            = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateSerializer() {
        this(null);
    }

    public CustomDateSerializer(Class t) {
        super(t);
    }

    @Override
    public void serialize (Date value, JsonGenerator gen, SerializerProvider arg2)
            throws IOException {
        gen.writeString(value.getDay() + "/" + value.getMonth());
    }
}

Chỉ định CustomDateSerializer cho eventDate field với @JsonSerializer annotation.

public class Event {
    public String name;
 
    @JsonSerialize(using = CustomDateSerializer.class)
    public Date eventDate;
    // ... 
}

Cuối cùng chúng ta sẽ kiểm thử kết quả:

public class Main {

    public static void main(String[] agrs) throws IOException, ParseException {

        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

        String toParse = "20-12-2014 02:30:00";
        Date date = df.parse(toParse);
        Event event = new Event("party", date);

        ObjectMapper mapper = new ObjectMapper();
        String result = mapper.writeValueAsString(event);
        System.out.println(result);

    }
}

Output:

{
    "name":"party",
    "eventDate":"6/11"
}

Serialize Java 8 Date với Jackson

Java 8 mở rộng với rất nhiều kiểu dữ liệu hỗ trợ thao tác với Date, trong phần này chúng ta sẽ sử dụng Module mà Jackson đã cung cấp để có thể tương thích với các dữ liệu Date trong Java 8.

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.9.7</version>
</dependency>

Sau khi download thư viện thành công, chúng ta cần đăng ký JavaTimeModule với Jackson, phần còn lại sẽ do Jackson xử lý.

Ví dụ serialize LocalDateTime sang Json.

public class Main {

    public static void main(String[] agrs) throws IOException {
        LocalDateTime date = LocalDateTime.of(2014, 12, 20, 2, 30);
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        String result = mapper.writeValueAsString(date);
        System.out.println(result);
    }
}

Deserialize Date

Trong phần này, chúng ta sẽ tìm cách deserialize Date trong Json về lại Java Date object.

public class Main {

    public static void main(String[] agrs) throws IOException {
        String json = "{\"name\":\"party\",\"eventDate\":\"20-12-2014 02:30:00\"}";

        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
        ObjectMapper mapper = new ObjectMapper();
        mapper.setDateFormat(df);

        Event event = mapper.readerFor(Event.class).readValue(json);
        System.out.println(event.getName() + " - " + event.getEventDate());
    }
}

Output: party – Sat Dec 20 02:30:00 ICT 2014

Custom Deserializer

Nếu có các yêu cầu riêng biệt cần tinh chỉnh mới có thể chuyển Json sang Java object chúng ta có thể tạo ra một custom deserializer với @JsonDeserializer annotation.

Ví dụ chúng ta có chuỗi Json nhưng eventDate chỉ chứa ngàytháng, khi deserialize chúng ta mong muốn mặc định năm sẽ là 2020

Trước tiên chúng ta cần tạo CustomDateDeserializer class như là một custom deserializer 

public class CustomDateDeserializer extends StdDeserializer<Date> {


    public CustomDateDeserializer() {
        this(null);
    }

    public CustomDateDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public Date deserialize(JsonParser jsonparser, DeserializationContext context)
            throws IOException {
        String date = jsonparser.getText();
        String[] dates = date.split("-");
        return new Date(2020, Integer.parseInt(dates[1]), Integer.parseInt(dates[0]));

    }
}

Tiếp theo chú thích cho eventDate sử dụng CustomDateDeserializer trong deserialization.

public class Event {
    public String name;
 
    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
    // ...
}

Cuối cùng tiến hành kiểm thử 

public class Main {

    public static void main(String[] agrs) throws IOException {
        String json = "{\"name\":\"party\",\"eventDate\":\"20-12\"}";

        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
        ObjectMapper mapper = new ObjectMapper();

        Event event = mapper.readerFor(Event.class).readValue(json);

        System.out.println(event.getName() + " - " + event.getEventDate());
    }
}

Output :party – Thu Jan 20 00:00:00 ICT 3921

Tóm lược

Định dạng Date trong serializer/deserializer Json là một một vấn đề đau đầu bởi có rất nhiều trường hợp chúng ta phải xử lý. Bài viết này đã cung cấp cho chúng ta nhiều cách tiếp cận khác nhau để các bạn có thể áp dụng trong các trường hợp cụ thể, giúp giải quyết nhanh chóng bài toán.

Nguồn tham khảo

https://www.baeldung.com/jackson-serialize-dates

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