Files
recordingtest/src/Recordingtest.DiffReporter/JsonDiffer.cs
2026-04-07 14:12:15 +09:00

78 lines
2.5 KiB
C#

using System.Text.Json;
namespace Recordingtest.DiffReporter;
internal static class JsonDiffer
{
public static IReadOnlyList<Hunk> Diff(JsonElement a, JsonElement b)
{
var aMap = new SortedDictionary<string, string>(StringComparer.Ordinal);
var bMap = new SortedDictionary<string, string>(StringComparer.Ordinal);
Flatten("$", a, aMap);
Flatten("$", b, bMap);
var hunks = new List<Hunk>();
var allKeys = new SortedSet<string>(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<string, string> 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;
}
}
}