sclass JsonDiff { srecord DiffResult(O a, O b) {} srecord Removed > DiffResult {} // b missing srecord Added > DiffResult {} // a missing srecord DifferentType > DiffResult {} srecord Different > DiffResult {} // same type, generally different // returns null if identical sO diff(S json1, S json2) { O o1 = jsonDecode(json1), o2 = jsonDecode(json2); ret diff(json1, json2); } sO diff(O a, O b) { if (eq(a, b)) null; if (a == null) ret Removed(a, b); if (b == null) ret Added(a, b); if (getClass(a) != getClass(b)) ret DifferentType(a, b); // from here on, we know that a and b have the same type if (a cast Map) ret mapKeyAndFunction_lhm(concatAsOrderedSet(keys(a), keys(b)), key -> diff(a.get(key), b/Map.get(key))); // TODO: lists ret Different(a, b); } }