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));
}
}