Article
· Feb 18, 2021 2m read

Basic JSON Compare

Hi Dev Community

I thought i would share a little method I knocked together to traverse and compare 2 JSON objects for basic equivilance.   I'm currently working on some data migration, and wanted a basic sanity check to validate that the JSON output is basically equivliant between the old and new, excluding a few things like timestamps.  

It's a basic little recurvsive method, that will bubble up any differences over a nested structure.   It's very low tech, as that's all I need it to do, but I thought it might be useful for others?

It can accept a source and target JSON object or array, and optionally a RefNo if you are wanting to batch run this, and track the instances of differences in a ^zKeyTrap global. Additionally. there's an exclusion list for any keys which would would always expect to be differnt, such as ModificationTimestamps

ClassMethod CompareJSON(source As %String, target As %String, RefNo As %String = 1) As %Boolean
{
set Identical = 1
Set propsIteratorsource = source.%GetIterator()
While (propsIteratorsource.%GetNext(.key,.value)) {
  //Check for differences, and optionally exclude some keys
  if (value '= target.%Get(key)&&("LastModifiedTime,LocalTime"'[key)) { 
   if (source.%GetTypeOf(key)="object")||(source.%GetTypeOf(key)="array"){
tSC= ..CompareJSON(value,target.%Get(key),RefNo)  
else {
!,"Source ",key,?30,value
!,"Target ",key,?30,target.%Get(key)
     //Store a reference if running in Batch mode, for later review
^zKeyTrap(key)=RefNo
     set tSC = 0 // A mismatch means key-value is not identical
   }
  // Multiply so that any zeros bubble up
  set Identical=(Identical*tSC) 
  }  
}    
Identical
}

Please feel free to use and adapt this if it would be helpful in your use of JSON

Discussion (5)1
Log in or sign up to continue

I think, your solution is an  "ad hoc" solution for a particular task, anyway, I want to point out two problems.

First, the solution fails, if the size of <source> is less then than the size of <target>:

set source={"Name":"Joe", "Age":50 }
set target={"Name":"Joe", "Age":50, "Phone":"123-456"}

write CompareJSON(source,target) ---> 1
write CompareJSON(target,source) ---> 0

The same goes for data like:

set source={"Name":"Joe", "Age":50, "Data":[10,20] }
set target={"Name":"Joe", "Age":50, "Data":[10,20,30]}

Maybe your data do not have such cases.

A quick check could be:

if source.%Size()-target.%Size() { quit "Size-problem" }

Second, in a more "general" case, comparing lists could sometimes lead to an philosophical question:
are two lists with the same elements but in a different sequence (of those elements) equal or not?

list1: [aaa, bbb]
list2: [bbb, aaa]

The answer MAY depend on the question, what is stored in those lists?

If the lists contains, for example, some kind of instructions, then the sequence of those instructions will mattern, but if those list are just list of, say my hobbies, then the sequence is unimportant (except, if one implies some kind of weighting, like the first entry is my preferred hobby).

This implies to me, to compare two list, you MAY need an extra info, something like:
dependsOnSequence = yes | no

One more hint, in the line

s tSC= ..CompareJSON(value,target.%Get(key),RefNo)  

what happens if target.%Get(key) is empty (or not a corresponding object)?

Just my2cents

Hi Julius

Thanks for your comments, and you are correct in assessing this is for a specific use case (I'm migrating data between 2 structures and want to make sure the exported JSON is EXACTLY the same).

For the first point, I really only care that all values in the source are also in the target.   If I wanted to be thorough, I woudl run the compare in both directions, but since I'm expecting that the target will contain additional properties, I only need a 1 way compare)

For the second point, I actually define the ordering of exporting of arrays explicitly, so I would expect a like for like compare.   For other cases, additional logic would need to be added, as you pointed out

For the last point,  this should return a mismatch, which is good enough for my use case, but again, might not be ideal for other use cases