json-canonicalizer/JsonCanonicalizer.BL/Canonicalizer.cs
2026-06-23 13:36:13 +03:00

113 lines
3.9 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Runtime.Serialization;
using System.Text.Json;
namespace JsonCanonicalizer.BL;
public class Canonicalizer
{
public static void WriteSorted(Utf8JsonWriter writer, JsonElement element)
{
switch (element.ValueKind)
{
case JsonValueKind.Object:
writer.WriteStartObject();
// Сортируем ключи объекта по имени
foreach (var property in element.EnumerateObject()
.OrderBy(p => p.Name, StringComparer.Ordinal))
{
writer.WritePropertyName(property.Name);
WriteSorted(writer, property.Value);
}
writer.WriteEndObject();
break;
case JsonValueKind.Array:
writer.WriteStartArray();
var items = element.EnumerateArray().ToList();
// Проверяем, нужно ли сортировать массив по guid:
// 1. Все элементы — объекты
// 2. У каждого объекта есть свойство "guid"
bool allAreObjects = items.All(i => i.ValueKind == JsonValueKind.Object);
bool allHaveGuid = allAreObjects &&
items.All(i => IsOrderable(i));
IEnumerable<JsonElement> orderedItems = items;
if (allHaveGuid)
{
orderedItems = items.OrderBy(i => GetOrder(i),
StringComparer.OrdinalIgnoreCase);
}
// Рекурсивно обрабатываем каждый элемент (на случай вложенных массивов)
foreach (var item in orderedItems)
{
WriteSorted(writer, item);
}
writer.WriteEndArray();
break;
case JsonValueKind.String:
writer.WriteStringValue(element.GetString());
break;
case JsonValueKind.Number:
if (element.TryGetDecimal(out decimal dec))
writer.WriteNumberValue(dec);
else
writer.WriteNumberValue(element.GetDouble());
break;
case JsonValueKind.True:
writer.WriteBooleanValue(true);
break;
case JsonValueKind.False:
writer.WriteBooleanValue(false);
break;
case JsonValueKind.Null:
writer.WriteNullValue();
break;
default:
writer.WriteCommentValue(element.GetRawText());
break;
}
}
static bool IsOrderable(JsonElement e)
{
return e.TryGetProperty("id", out _) || e.TryGetProperty("guid", out _);
}
static string GetOrder(JsonElement e)
{
if (e.TryGetProperty("id", out JsonElement id))
{
return GetGuidValue(id);
}
else
{
return GetGuidValue(e.GetProperty("guid"));
}
}
/// <summary>
/// Универсально извлекает значение guid как строку для сравнения.
/// Работает даже если guid почему-то оказался числом или булевым.
/// </summary>
static string GetGuidValue(JsonElement guidElement)
{
return guidElement.ValueKind switch
{
JsonValueKind.String => guidElement.GetString() ?? "",
JsonValueKind.Number => guidElement.GetRawText(),
JsonValueKind.True => "true",
JsonValueKind.False => "false",
JsonValueKind.Null => "",
_ => guidElement.GetRawText()
};
}
}