using System.Text.Json; namespace Recordingtest.DiffReporter; internal static class JsonDiffer { public static IReadOnlyList Diff(JsonElement a, JsonElement b) { var aMap = new SortedDictionary(StringComparer.Ordinal); var bMap = new SortedDictionary(StringComparer.Ordinal); Flatten("$", a, aMap); Flatten("$", b, bMap); var hunks = new List(); var allKeys = new SortedSet(aMap.Keys, StringComparer.Ordinal); foreach (var k in bMap.Keys) allKeys.Add(k); int idx = 0; foreach (var key in allKeys) { var hasA = aMap.TryGetValue(key, out var av); var hasB = bMap.TryGetValue(key, out var bv); if (hasA && hasB) { if (!string.Equals(av, bv, StringComparison.Ordinal)) hunks.Add(new Hunk(idx, "changed", $"{key}={av}", $"{key}={bv}")); } else if (hasA) { hunks.Add(new Hunk(idx, "removed", $"{key}={av}", string.Empty)); } else if (hasB) { hunks.Add(new Hunk(idx, "added", string.Empty, $"{key}={bv}")); } idx++; } return hunks; } private static void Flatten(string path, JsonElement el, IDictionary map) { switch (el.ValueKind) { case JsonValueKind.Object: foreach (var p in el.EnumerateObject()) Flatten(path + "." + p.Name, p.Value, map); break; case JsonValueKind.Array: int i = 0; foreach (var item in el.EnumerateArray()) { Flatten(path + "[" + i + "]", item, map); i++; } break; case JsonValueKind.String: map[path] = "\"" + el.GetString() + "\""; break; case JsonValueKind.Number: map[path] = el.GetRawText(); break; case JsonValueKind.True: map[path] = "true"; break; case JsonValueKind.False: map[path] = "false"; break; case JsonValueKind.Null: map[path] = "null"; break; default: map[path] = el.GetRawText(); break; } } }