Friday, 26 March 2021

Compare JSON documents in Java

In this post, I am going to explain how to compare two json documents in Java.

 

Approach 1: Using Guava’s Maps.difference(Map<K, V>, Map<K, V>) method.

 

a. Convert json string to a map

 

Type type = new TypeToken<Map<String, Object>>() {}.getType();

Map<String, Object> leftMap = gson.fromJson(json1, type);

 

b. Use ‘Maps.difference’ method to get the differences between two maps.

MapDifference<String, Object> difference = Maps.difference(leftMap, rightMap);

 

c. Print MapDifference object.

private static void printMapDifference(MapDifference<String, Object> difference) {
	System.out.println("\n\nEntries only on left\n--------------------------");
	difference.entriesOnlyOnLeft().forEach((key, value) -> System.out.println(key + ": " + value));

	System.out.println("\n\nEntries only on right\n--------------------------");
	difference.entriesOnlyOnRight().forEach((key, value) -> System.out.println(key + ": " + value));

	System.out.println("\n\nEntries differing\n--------------------------");
	difference.entriesDiffering().forEach((key, value) -> System.out.println(key + ": " + value));

	System.out.println("\n\nEntries in common\n--------------------------");
	difference.entriesInCommon().forEach((key, value) -> System.out.println(key + ": " + value));
}

 

Find the below working application.

 

Employee.java

package com.sample.app;

public class Employee {
	private int id;
	private String firstName;
	private String lastName;
	private Integer age;

	public Employee() {

	}

	public Employee(int id, String firstName, String lastName) {
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
	}

	public Employee(int id, String firstName, String lastName, Integer age) {
		this(id, firstName, lastName);
		this.age = age;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

}

 

App.java

package com.sample.app;

import java.lang.reflect.Type;
import java.util.Map;

import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

public class App {

	private static void printMapDifference(MapDifference<String, Object> difference) {
		System.out.println("\n\nEntries only on left\n--------------------------");
		difference.entriesOnlyOnLeft().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries only on right\n--------------------------");
		difference.entriesOnlyOnRight().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries differing\n--------------------------");
		difference.entriesDiffering().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries in common\n--------------------------");
		difference.entriesInCommon().forEach((key, value) -> System.out.println(key + ": " + value));
	}

	public static void main(String args[]) {
		Employee emp1 = new Employee(1, "Krishna", "Gurram", 31);
		Employee emp2 = new Employee(1, "Ram", "Gurram");

		Gson gson = new GsonBuilder().setPrettyPrinting().create();
		String json1 = gson.toJson(emp1);
		String json2 = gson.toJson(emp2);

		System.out.println(json1);
		System.out.println(json2);

		Type type = new TypeToken<Map<String, Object>>() {
		}.getType();

		Map<String, Object> leftMap = gson.fromJson(json1, type);
		Map<String, Object> rightMap = gson.fromJson(json2, type);

		MapDifference<String, Object> difference = Maps.difference(leftMap, rightMap);
		printMapDifference(difference);
	}

}

 

Output

{
  "id": 1,
  "firstName": "Krishna",
  "lastName": "Gurram",
  "age": 31
}
{
  "id": 1,
  "firstName": "Ram",
  "lastName": "Gurram"
}


Entries only on left
--------------------------
age: 31.0


Entries only on right
--------------------------


Entries differing
--------------------------
firstName: (Krishna, Ram)


Entries in common
--------------------------
lastName: Gurram
id: 1.0

 

Problem with above approach

When it comes to the nested objects, the output doesn’t look meaningful.

 

Let us see this with an example.

 

Address.java 

package com.sample.app;

public class Address {

	private String city;
	private String country;
	private String street;

	public Address() {

	}

	public Address(String street, String city, String country) {
		this.city = city;
		this.country = country;
		this.street = street;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	public String getCountry() {
		return country;
	}

	public void setCountry(String country) {
		this.country = country;
	}

	public String getStreet() {
		return street;
	}

	public void setStreet(String street) {
		this.street = street;
	}

}

 

Employee.java

package com.sample.app;

import java.util.List;

public class Employee {
	private int id;
	private String firstName;
	private String lastName;
	private Integer age;
	private List<Address> addresses;

	public Employee() {

	}

	public Employee(int id, String firstName, String lastName) {
		this.id = id;
		this.firstName = firstName;
		this.lastName = lastName;
	}

	public Employee(int id, String firstName, String lastName, Integer age) {
		this(id, firstName, lastName);
		this.age = age;
	}

	public Employee(int id, String firstName, String lastName, Integer age, List<Address> addresses) {
		this(id, firstName, lastName, age);
		this.addresses = addresses;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public List<Address> getAddresses() {
		return addresses;
	}

	public void setAddresses(List<Address> addresses) {
		this.addresses = addresses;
	}

}

 

App.java

package com.sample.app;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Map;

import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.util.*;

public class App {

	private static void printMapDifference(MapDifference<String, Object> difference) {
		System.out.println("\n\nEntries only on left\n--------------------------");
		difference.entriesOnlyOnLeft().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries only on right\n--------------------------");
		difference.entriesOnlyOnRight().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries differing\n--------------------------");
		difference.entriesDiffering().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries in common\n--------------------------");
		difference.entriesInCommon().forEach((key, value) -> System.out.println(key + ": " + value));
	}

	public static void main(String args[]) {

		Address addr1 = new Address("Chowdeswari street", "Bangalore", "India");
		Address addr2 = new Address("Ramappa street", "Hyderabad", "India");
		Address addr3 = new Address("PTR street", "Gudivada", "India");

		List<Address> addresses1 = Arrays.asList(addr1, addr2);
		List<Address> addresses2 = Arrays.asList(addr1, addr3);

		Employee emp1 = new Employee(1, "Krishna", "Gurram", 31, addresses1);
		Employee emp2 = new Employee(1, "Ram", "Gurram", 24, addresses2);

		Gson gson = new GsonBuilder().setPrettyPrinting().create();
		String json1 = gson.toJson(emp1);
		String json2 = gson.toJson(emp2);

		System.out.println(json1);
		System.out.println(json2);

		Type type = new TypeToken<Map<String, Object>>() {
		}.getType();

		Map<String, Object> leftMap = gson.fromJson(json1, type);
		Map<String, Object> rightMap = gson.fromJson(json2, type);

		MapDifference<String, Object> difference = Maps.difference(leftMap, rightMap);
		printMapDifference(difference);
		
		
	}

}

 

Output

{
  "id": 1,
  "firstName": "Krishna",
  "lastName": "Gurram",
  "age": 31,
  "addresses": [
    {
      "city": "Bangalore",
      "country": "India",
      "street": "Chowdeswari street"
    },
    {
      "city": "Hyderabad",
      "country": "India",
      "street": "Ramappa street"
    }
  ]
}
{
  "id": 1,
  "firstName": "Ram",
  "lastName": "Gurram",
  "age": 24,
  "addresses": [
    {
      "city": "Bangalore",
      "country": "India",
      "street": "Chowdeswari street"
    },
    {
      "city": "Gudivada",
      "country": "India",
      "street": "PTR street"
    }
  ]
}


Entries only on left
--------------------------


Entries only on right
--------------------------


Entries differing
--------------------------
firstName: (Krishna, Ram)
addresses: ([{city=Bangalore, country=India, street=Chowdeswari street}, {city=Hyderabad, country=India, street=Ramappa street}], [{city=Bangalore, country=India, street=Chowdeswari street}, {city=Gudivada, country=India, street=PTR street}])
age: (31.0, 24.0)


Entries in common
--------------------------
lastName: Gurram
id: 1.0

 

We can get more meaningful output by flattening the map.

 

FlatMapUtil.java

 

package com.sample.app;

import java.util.AbstractMap.SimpleEntry;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.IntStream;
import java.util.stream.Stream;

public class FlatMapUtil {
  public static Map<String, Object> flatten(Map<String, Object> mapToFlatten) {
    return mapToFlatten.entrySet().stream().flatMap(FlatMapUtil::flatten).collect(LinkedHashMap::new,
        (map, entry) -> map.put(entry.getKey(), entry.getValue()), LinkedHashMap::putAll);
  }

  private static Stream<Entry<String, Object>> flatten(Map.Entry<String, Object> entry) {

    if (entry == null) {
      return Stream.empty();
    }

    Object value = entry.getValue();

    if (value instanceof Map<?, ?>) {
      Map<?, ?> properties = (Map<?, ?>) value;
      return properties.entrySet().stream()
          .flatMap(e -> flatten(new SimpleEntry<>(entry.getKey() + "." + e.getKey(), e.getValue())));
    }

    if (value instanceof List<?>) {
      List<?> list = (List<?>) value;
      return IntStream.range(0, list.size())
          .mapToObj(i -> new SimpleEntry<String, Object>(entry.getKey() + "[" + i + "]", list.get(i)))
          .flatMap(FlatMapUtil::flatten);
    }

    return Stream.of(entry);
  }
}

App.java

package com.sample.app;

import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Map;

import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import java.util.*;

public class App {

	private static void printMapDifference(MapDifference<String, Object> difference) {
		System.out.println("\n\nEntries only on left\n--------------------------");
		difference.entriesOnlyOnLeft().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries only on right\n--------------------------");
		difference.entriesOnlyOnRight().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries differing\n--------------------------");
		difference.entriesDiffering().forEach((key, value) -> System.out.println(key + ": " + value));

		System.out.println("\n\nEntries in common\n--------------------------");
		difference.entriesInCommon().forEach((key, value) -> System.out.println(key + ": " + value));
	}

	public static void main(String args[]) {

		Address addr1 = new Address("Chowdeswari street", "Bangalore", "India");
		Address addr2 = new Address("Ramappa street", "Hyderabad", "India");
		Address addr3 = new Address("PTR street", "Gudivada", "India");

		List<Address> addresses1 = Arrays.asList(addr1, addr2);
		List<Address> addresses2 = Arrays.asList(addr1, addr3);

		Employee emp1 = new Employee(1, "Krishna", "Gurram", 31, addresses1);
		Employee emp2 = new Employee(1, "Ram", "Gurram", 24, addresses2);

		Gson gson = new GsonBuilder().setPrettyPrinting().create();
		String json1 = gson.toJson(emp1);
		String json2 = gson.toJson(emp2);

		System.out.println(json1);
		System.out.println(json2);

		Type type = new TypeToken<Map<String, Object>>() {
		}.getType();

		Map<String, Object> leftMap = gson.fromJson(json1, type);
		Map<String, Object> rightMap = gson.fromJson(json2, type);

		leftMap = FlatMapUtil.flatten(leftMap);
		rightMap = FlatMapUtil.flatten(rightMap);
		MapDifference<String, Object> difference = Maps.difference(leftMap, rightMap);
		printMapDifference(difference);

	}

}

Output

{
  "id": 1,
  "firstName": "Krishna",
  "lastName": "Gurram",
  "age": 31,
  "addresses": [
    {
      "city": "Bangalore",
      "country": "India",
      "street": "Chowdeswari street"
    },
    {
      "city": "Hyderabad",
      "country": "India",
      "street": "Ramappa street"
    }
  ]
}
{
  "id": 1,
  "firstName": "Ram",
  "lastName": "Gurram",
  "age": 24,
  "addresses": [
    {
      "city": "Bangalore",
      "country": "India",
      "street": "Chowdeswari street"
    },
    {
      "city": "Gudivada",
      "country": "India",
      "street": "PTR street"
    }
  ]
}


Entries only on left
--------------------------


Entries only on right
--------------------------


Entries differing
--------------------------
firstName: (Krishna, Ram)
addresses[1].city: (Hyderabad, Gudivada)
addresses[1].street: (Ramappa street, PTR street)
age: (31.0, 24.0)


Entries in common
--------------------------
addresses[0].city: Bangalore
lastName: Gurram
addresses[0].street: Chowdeswari street
addresses[0].country: India
id: 1.0
addresses[1].country: India


 

Previous                                                    Next                                                    Home

No comments:

Post a Comment