Refactor code structure for improved readability and maintainability; optimize performance in key functions.

This commit is contained in:
master
2025-12-22 19:06:31 +02:00
parent dfaa2079aa
commit 4602ccc3a3
1444 changed files with 109919 additions and 8058 deletions

View File

@@ -0,0 +1,95 @@
using System.Globalization;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace StellaOps.Canonicalization.Json;
/// <summary>
/// Produces canonical JSON output with deterministic ordering.
/// Implements RFC 8785 principles for stable output.
/// </summary>
public static class CanonicalJsonSerializer
{
private static readonly JsonSerializerOptions Options = new()
{
WriteIndented = false,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
NumberHandling = JsonNumberHandling.Strict,
Converters =
{
new StableDictionaryConverterFactory(),
new Iso8601DateTimeConverter()
}
};
public static string Serialize<T>(T value)
=> JsonSerializer.Serialize(value, Options);
public static (string Json, string Digest) SerializeWithDigest<T>(T value)
{
var json = Serialize(value);
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(json));
var digest = Convert.ToHexString(hash).ToLowerInvariant();
return (json, digest);
}
public static T Deserialize<T>(string json)
{
return JsonSerializer.Deserialize<T>(json, Options)
?? throw new InvalidOperationException($"Failed to deserialize {typeof(T).Name}");
}
}
/// <summary>
/// Converter factory that orders dictionary keys alphabetically.
/// </summary>
public sealed class StableDictionaryConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType) return false;
var generic = typeToConvert.GetGenericTypeDefinition();
return generic == typeof(Dictionary<,>) || generic == typeof(IDictionary<,>) || generic == typeof(IReadOnlyDictionary<,>);
}
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
var args = typeToConvert.GetGenericArguments();
var converterType = typeof(StableDictionaryConverter<,>).MakeGenericType(args[0], args[1]);
return (JsonConverter)Activator.CreateInstance(converterType)!;
}
}
public sealed class StableDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
where TKey : notnull
{
public override IDictionary<TKey, TValue>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> JsonSerializer.Deserialize<Dictionary<TKey, TValue>>(ref reader, options);
public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options)
{
writer.WriteStartObject();
foreach (var kvp in value.OrderBy(x => x.Key?.ToString(), StringComparer.Ordinal))
{
writer.WritePropertyName(kvp.Key?.ToString() ?? string.Empty);
JsonSerializer.Serialize(writer, kvp.Value, options);
}
writer.WriteEndObject();
}
}
/// <summary>
/// Converter for ISO 8601 date/time with UTC normalization.
/// </summary>
public sealed class Iso8601DateTimeConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> DateTimeOffset.Parse(reader.GetString()!, CultureInfo.InvariantCulture);
public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
=> writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture));
}