Mục lục
Trong bài viết này, chúng ta sẽ cùng nhau tìm hiểu cách so sánh 2 Json object với Jackson trong Java.
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.
Sử dụng Jackson để so sánh 2 Json object
Giả sử có 2 Json string được đọc ra từ Json file, hay được tạo sẵn. Chúng ta có thể sử dụng ObjectMapper để đọc Json string vào JsonNode object để so sánh.
So sánh Json object
Sau khi đọc Json string vào JsonNode object, chúng ta có thể sử dụng JsonNode.equals() method để so sánh 2 JsonNode với nhau.
Ví dụ có chuỗi Json s1
{ "employee": { "id": "1212", "fullName": "John Miles", "age": 34 } }
Và Json s2
{ "employee": { "id": "1212", "age": 34, "fullName": "John Miles" } }
Tiến hành đọc chúng vào JsonNode object và so sánh.
public class Main { public static void main(String[] agrs) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String s1 = "{\n" + " \"employee\":\n" + " {\n" + " \"id\": \"1212\",\n" + " \"fullName\": \"John Miles\",\n" + " \"age\": 34\n" + " }\n" + "}\n"; String s2 = "{ \n" + " \"employee\":\n" + " {\n" + " \"id\": \"1212\",\n" + " \"age\": 34,\n" + " \"fullName\": \"John Miles\"\n" + " }\n" + "}\n"; JsonNode jsonNode1 = objectMapper.readTree(s1); JsonNode jsonNode2 = objectMapper.readTree(s2); System.out.println(jsonNode1.equals(jsonNode2)); // TRUE } }
Note: mặc dù thứ thự các thuộc tính trong Json string khác nhau, nhưng chúng vẫn xác định là bằng nhau. JsonNode.equals() không quan tâm đến thứ tự mà đi so sánh giá trị từng thuộc tính với nhau.
So sánh Json object cấu trúc lồng
Nếu chúng ta có các Json object cấu trúc lồng s1
{ "employee": { "id": "1212", "fullName":"John Miles", "age": 34, "contact": { "email": "[email protected]", "phone": "9999999999" } } }
và s2
{ "employee": { "id": "1212", "age": 34, "fullName": "John Miles", "contact": { "email": "[email protected]", "phone": "9999999999" } } }
Chúng ta vẫn có thể sử dụng equals() method để so sánh 2 Json object này
public class Main { public static void main(String[] agrs) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String s1 = "{\n" + "\"employee\":{\n" + "\"id\":\"1212\",\n" + "\"fullName\":\"John Miles\",\n" + "\"age\":34,\n" + "\"contact\":{\n" + "\"email\":\"[email protected]\",\n" + "\"phone\":\"9999999999\"\n" + "}\n" + "}\n" + "}"; String s2 = "{\n" + "\"employee\":{\n" + "\"id\":\"1212\",\n" + "\"age\":34,\n" + "\"fullName\":\"John Miles\",\n" + "\"contact\":{\n" + "\"email\":\"[email protected]\",\n" + "\"phone\":\"9999999999\"\n" + "}\n" + "}\n" + "}"; JsonNode jsonNode1 = objectMapper.readTree(s1); JsonNode jsonNode2 = objectMapper.readTree(s2); System.out.println(jsonNode1.equals(jsonNode2)); // TRUE } }
So sánh Json Object chứa mảng phần tử
Tương tự như 2 trường hợp trên, chúng ta vẫn sử dụng equals() method, thế nhưng chú ý rằng các phần tử trong mảng phải cùng thứ tự mới được xem là bằng nhau.
package com.chat.socket.server; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import java.io.IOException; import java.util.Optional; class Book { String title; Optional<String> subTitle; public Book() { } public Book(String title, Optional<String> subTitle) { this.title = title; this.subTitle = subTitle; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Optional<String> getSubTitle() { return subTitle; } public void setSubTitle(Optional<String> subTitle) { this.subTitle = subTitle; } } public class Main { public static void main(String[] agrs) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String s1 = "{\n" + " \"employee\":\n" + " {\n" + " \"id\": \"1212\",\n" + " \"fullName\": \"John Miles\",\n" + " \"age\": 34,\n" + " \"skills\": [\"Java\", \"C++\", \"Python\"]\n" + " }\n" + "}\n"; String s2 = "{\n" + " \"employee\":\n" + " {\n" + " \"id\": \"1212\",\n" + " \"age\": 34,\n" + " \"fullName\": \"John Miles\",\n" + " \"skills\": [\"Java\", \"C++\", \"Python\"] \n" + " } \n" + "}\n"; JsonNode jsonNode1 = objectMapper.readTree(s1); JsonNode jsonNode2 = objectMapper.readTree(s2); System.out.println(jsonNode1.equals(jsonNode2)); // TRUE } }
So sánh Json object với Custom Comparator
JsonNode.equals() method hoạt động tốt trong đa số trường hợp. Tuy nhiên Jackson còn cung cấp thêm một override equals() method khác là JsonNode.equals(comparator, JsonNode) dùng trong các trường hợp đặc biệt, chúng ta phải implement Comparator được sử dụng trong quá trình so sánh.
Custom Comaparator cho Numeric
Giả sử chúng ta có Json s1:
{ "name": "John", "score": 5.0 }
Đem so sánh với Json s2:
{ "name": "John", "score": 5 }
Trong trường hợp này, đối với chúng ta 5 và 5.0 phải bằng nhau, tuy nhiên JsonNode.equals() không nghĩ vậy
public class Main { public static void main(String[] agrs) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String s1 = "{\n" + " \"name\": \"John\",\n" + " \"score\": 5.0\n" + "}\n"; String s2 = "{\n" + " \"name\": \"John\",\n" + " \"score\": 5\n" + "}\n"; JsonNode jsonNode1 = objectMapper.readTree(s1); JsonNode jsonNode2 = objectMapper.readTree(s2); System.out.println(jsonNode1.equals(jsonNode2)); //FALSE } }
Trong trường hợp này, chúng ta sẽ cần custom 1 Comparator để so sánh 5 và 5.0 cho kết quả bằng nhau.
public class NumericNodeComparator implements Comparator<JsonNode> { @Override public int compare(JsonNode o1, JsonNode o2) { if (o1.equals(o2)){ return 0; } if ((o1 instanceof NumericNode) && (o2 instanceof NumericNode)){ Double d1 = ((NumericNode) o1).asDouble(); Double d2 = ((NumericNode) o2).asDouble(); if (d1.compareTo(d2) == 0) { return 0; } } return 1; } }
Tiếp theo, chúng ta sẽ sử dụng NumericNodeComparator khi so sánh Json s1 và s2.
public class Main { public static void main(String[] agrs) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String s1 = "{\n" + " \"name\": \"John\",\n" + " \"score\": 5.0\n" + "}\n"; String s2 = "{\n" + " \"name\": \"John\",\n" + " \"score\": 5\n" + "}\n"; JsonNode jsonNode1 = objectMapper.readTree(s1); JsonNode jsonNode2 = objectMapper.readTree(s2); NumericNodeComparator cmp = new NumericNodeComparator(); System.out.println(jsonNode1.equals(cmp, jsonNode2)); // TRUE } }
Custom Comaparator cho Text value
Lấy ví dụ chúng ta muốn so sánh 2 chuỗi trong trường hơp insensitive – không phân biệt hoa thường.
Json s1
{ "name": "john", "score": 5 }
Và Json s2
{ "name": "JOHN", "score": 5 }
Tiếp theo tiến tiến hành tạo TextNodeComparator
public class TextNodeComparator implements Comparator<JsonNode> { @Override public int compare(JsonNode o1, JsonNode o2) { if (o1.equals(o2)) { return 0; } if ((o1 instanceof TextNode) && (o2 instanceof TextNode)) { String s1 = ((TextNode) o1).asText(); String s2 = ((TextNode) o2).asText(); if (s1.equalsIgnoreCase(s2)) { return 0; } } return 1; } }
Cuối cùng, sử dụng TextNodeComparator để so sánh 2 Json string với nhau không phân biệt chữ hoa với chữ thường đối với dữ liệu dạng text.
public class Main { public static void main(String[] agrs) throws IOException { ObjectMapper objectMapper = new ObjectMapper(); String s1 = "{\n" + " \"name\": \"john\", \n" + " \"score\": 5 \n" + "}\n"; String s2 = "{ \n" + " \"name\": \"JOHN\", \n" + " \"score\": 5 \n" + "}\n"; JsonNode jsonNode1 = objectMapper.readTree(s1); JsonNode jsonNode2 = objectMapper.readTree(s2); TextNodeComparator cmp = new TextNodeComparator(); System.out.println(jsonNode1.equals(cmp, jsonNode2)); // TRUE } }
Tóm lược
Như vậy, qua bài viết này chúng ta đã tìm hiểu về cách so sánh 2 Json object trong Java với Jackson. Custom comparator là một phần quan trọng khi cấu trúc, hoặc định dạng dữ liệu giữa 2 Json object khác nhau.