using System.Text.Json; using System.Text.Json.Serialization; namespace AVEVA.Historian.Client.StoreForward; /// /// Shared JSON settings for persisting envelopes. The custom /// normalizes HistorianEvent.Properties values (typed /// ) back to plain CLR scalars on read instead of leaving raw /// s, so a rehydrated event matches what the producer enqueued. /// internal static class OutboxJson { public static readonly JsonSerializerOptions Options = CreateOptions(); private static JsonSerializerOptions CreateOptions() { var options = new JsonSerializerOptions { WriteIndented = false, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; options.Converters.Add(new ObjectConverter()); return options; } private sealed class ObjectConverter : JsonConverter { public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { switch (reader.TokenType) { case JsonTokenType.Null: return null; case JsonTokenType.String: return reader.GetString(); case JsonTokenType.True: return true; case JsonTokenType.False: return false; case JsonTokenType.Number: return reader.TryGetInt64(out long l) ? l : reader.GetDouble(); default: using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) { return doc.RootElement.GetRawText(); } } } public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options) { if (value is null) { writer.WriteNullValue(); return; } // Serialize by runtime type so scalars use their own converters (no re-entry into this one). JsonSerializer.Serialize(writer, value, value.GetType(), options); } } }