Mục lục
Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu cách Jackson serialization và deserialization Java Map.
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.
Map Serialization
Java Map là một collection dạng key-value thường được xem là đối tượng không thân thiện trong serialization. Trong phần này chúng ta sẽ chia Map ra các dạng khác nhau.
Map<String, String> Serialization
Đối với trường hợp đơn giản này, chúng ta chỉ cần khởi tạo một Map<String, String> và serializtion sang Json
Map<String, String> map = new HashMap<>(); map.put("key", "value"); ObjectMapper mapper = new ObjectMapper(); String jsonResult = mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(map);
Output
{ "key" : "value" }
Map<Object, String> Serialization
Chúng ta có thể serialize một map chứa key là một Java class tự định nghĩa với một vài bước bổ sung.
Khởi tạo một MyPair class đại diện cho key của map
public class MyPair { private String first; private String second; public MyPair(String first, String second) { this.first = first; this.second = second; } public String getFirst() { return first; } public void setFirst(String first) { this.first = first; } public String getSecond() { return second; } public void setSecond(String second) { this.second = second; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof MyPair)) return false; MyPair myPair = (MyPair) o; return Objects.equals(first, myPair.first) && Objects.equals(second, myPair.second); } @Override public int hashCode() { return Objects.hash(first, second); } @Override @JsonValue public String toString() { return first + " and " + second; } }
Note: Chú thích @JsonValue cho toString() method của MyPair để đảm bảo Jackson sẽ sử dụng toString() khi serialize MyPair object.
public class Main { public static void main(String[] agrs) throws IOException { ObjectMapper mapper = new ObjectMapper(); Map<MyPair, String> map = new HashMap<>(); MyPair key = new MyPair("Abbott", "Costello"); map.put(key, "Comedy"); String jsonResult = mapper.writerWithDefaultPrettyPrinter() .writeValueAsString(map); System.out.println(jsonResult); } }
Output:
{ "Abbott and Costello" : "Comedy" }
Map<Object, Object> Serialization
Một trường hợp phức tạp hơn đó là cả key-value đều là Java class do chúng ta tự định nghĩa. Tuy nhiên đừng lo lắng, vì toString() method của MyPair class đã được chú thích @JsonValue, nên việc serialize value sang Json cũng tương tự key trong map.
public class Main { public static void main(String[] agrs) throws IOException { Map<MyPair, MyPair> map = new HashMap<>(); MyPair mapKey = new MyPair("Abbott", "Costello"); MyPair mapValue = new MyPair("Comedy", "1940s"); map.put(mapKey, mapValue); String jsonResult = new ObjectMapper().writerWithDefaultPrettyPrinter() .writeValueAsString(map); System.out.println(jsonResult); } }
Output:
"Abbott and Costello" : "Comedy and 1940s" }
Nếu không sử dụng @JsonValue cho MyPair#toString() method, thì toString() vẫn sẽ được áp dụng cho key của map khi serialize.
public class MyPair { @Override // @JsonValue public String toString() { return first + " and " + second; } // .... }
Thì khi serialize Map<MyPair, MyPair> giá trị của value sẽ có cấu trúc tương tự map trong Java.
public class Main { public static void main(String[] agrs) throws IOException { Map<MyPair, MyPair> map = new HashMap<>(); MyPair mapKey = new MyPair("Abbott", "Costello"); MyPair mapValue = new MyPair("Comedy", "1940s"); map.put(mapKey, mapValue); String jsonResult = new ObjectMapper().writerWithDefaultPrettyPrinter() .writeValueAsString(map); System.out.println(jsonResult); } }
Output
{ "Abbott and Costello" : { "first" : "Comedy", "second" : "1940s" } }
Map Deserialization
Map<String, String> Deserialization
Tương tự, với trường hợp đơn giản nhất của map khi deserialization. Lưu ý trong phần này, chúng ta sẽ sử dụng TypeReference để bao ngoài HashMap class để sử dụng trong readValue().
public class Main { public static void main(String[] agrs) throws IOException { String jsonInput = "{\"key\": \"value\"}"; TypeReference<HashMap<String, String>> typeRef = new TypeReference<HashMap<String, String>>() {}; Map<String, String> map = new ObjectMapper() .readValue(jsonInput, typeRef); System.out.println(map.get("Key")); // value } }
Map<Object, String> Deserialization
Đối với Map<Object, String> chúng ta có thể deserialize thông qua một số bước bổ sung.
Đối với các Json string được serialize ở phần trên, chúng đã sử dụng toString() method để lấy giá trị trong serialization với cấu trúc :
frist and second
Như vậy, việc đầu tiên chúng ta cần làm là khởi tạo một constructor nhận một String với cấu trúc trên và tiến hành tách chuỗi để có được first và second.
public MyPair { // .... public MyPair(String both) { String[] pairs = both.split("and"); this.first = pairs[0].trim(); this.second = pairs[1].trim(); } }
Tiếp theo, chúng ta cần chỉ định 1 custom deserializer sử dụng khi khi deserialize Json sang Map<MyPair, String>.
public class MyPairDeserializer extends KeyDeserializer { @Override public MyPair deserializeKey( String key, DeserializationContext ctxt) { return new MyPair(key); } }
Chỉ định MyPairDeserializer cho map với @JsonDeserialize và tiến hành kiểm thử.
public class Main { @JsonDeserialize(keyUsing = MyPairDeserializer.class) private static TypeReference<HashMap<MyPair, String>> typeRef; public static void main(String[] agrs) throws IOException { String jsonInput = "{\"Abbott and Costello\" : \"Comedy\"}"; typeRef = new TypeReference<HashMap<MyPair, String>>() {}; Map<MyPair, String> map = new ObjectMapper().readValue(jsonInput, typeRef); MyPair key = map.keySet().iterator().next(); System.out.println(key.toString()); System.out.println(map.get(key)); } }
Output:
Abbott and Costello
Comedy
Map<Object, Object> Deserialization
Phần cuối cùng, thay đổi cấu trúc của Map<Object, Object> với key và value đều là các class được định nghĩa.
Thật may, chúng ta sẽ không cần làm gì thêm, MyPairDeserializer sẽ áp dụng cho cả key-value dạng MyPair.
public class Main { @JsonDeserialize(keyUsing = MyPairDeserializer.class) private static TypeReference<HashMap<MyPair, String>> typeRef; public static void main(String[] agrs) throws IOException { String jsonInput = "{\"Abbott and Costello\" : \"Comedy and 1940s\"}"; TypeReference<HashMap<MyPair, MyPair>> typeRef = new TypeReference<HashMap<MyPair, MyPair>>() {}; Map<MyPair,MyPair> map = new ObjectMapper().readValue(jsonInput, typeRef); MyPair key = map.keySet().iterator().next(); System.out.println(key.toString()); System.out.println(map.get(key).toString()); } }
Output:
Abbott and Costello
Comedy and 1940s
Tóm lược
Như vậy là chúng ta đã biết cách serialize và deserialize Java Map trong Jackson. Đây là một dạng dữ liệu yêu cầu nhiều xử lý khi làm việc với Json, thông thường chúng ta phải thống nhất cấu trúc của Json khi serialize/deserialize để không gây ra các lỗi không đáng có.
Nguồn tham khảo
https://www.baeldung.com/jackson-maphttps://www.baeldung.com/jackson-map