using System.Security.Cryptography; using System.Text; using System.Text.Encodings.Web; using System.Text.Json; using System.Text.Json.Serialization; using static StellaOps.Localization.T; namespace StellaOps.Canonicalization.Json; /// /// Produces canonical JSON output with deterministic ordering. /// Implements RFC 8785 principles for stable output. /// 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 value) => JsonSerializer.Serialize(value, _options); public static (string Json, string Digest) SerializeWithDigest(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(string json) { return JsonSerializer.Deserialize(json, _options) ?? throw new InvalidOperationException(_t("common.canonicalization.deserialize_failed", typeof(T).Name)); } }