From 8ff875435be8643d79b366bb0e7216cf40f5037e Mon Sep 17 00:00:00 2001 From: softworkz Date: Mon, 17 Nov 2025 13:10:31 +0100 Subject: [PATCH] Fix API break: public API must not expose JsonElement objects This changes deserialization to the way how it was with Json.Net: Only .net primitive types are exposed publicly --- src/ElectronNET.API/API/IpcMain.cs | 28 +++- .../JsonToBoxedPrimitivesConverter.cs | 126 ++++++++++++++++++ 2 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 src/ElectronNET.API/Serialization/JsonToBoxedPrimitivesConverter.cs diff --git a/src/ElectronNET.API/API/IpcMain.cs b/src/ElectronNET.API/API/IpcMain.cs index 239098b..518bfc6 100644 --- a/src/ElectronNET.API/API/IpcMain.cs +++ b/src/ElectronNET.API/API/IpcMain.cs @@ -1,12 +1,13 @@ -using ElectronNET.API.Serialization; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using System.Threading.Tasks; - namespace ElectronNET.API { + using System; + using System.Diagnostics; + using System.Linq; + using System.Text.Json; + using System.Text.Json.Serialization; + using System.Threading.Tasks; + using ElectronNET.Serialization; + /// /// Communicate asynchronously from the main process to renderer processes. /// @@ -14,6 +15,18 @@ namespace ElectronNET.API { private static IpcMain _ipcMain; private static object _syncRoot = new object(); + private static readonly JsonSerializerOptions BoxedObjectSerializationOptions = new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + WriteIndented = false, + Converters = + { + new JsonStringEnumConverter(JsonNamingPolicy.CamelCase), + new JsonToBoxedPrimitivesConverter(), + } + }; + internal IpcMain() { @@ -65,6 +78,7 @@ namespace ElectronNET.API private static List FormatArguments(JsonElement args) { + var objectArray = args.Deserialize(BoxedObjectSerializationOptions).ToList(); var objectArray = args.Deserialize(ElectronJson.Options).ToList(); objectArray.RemoveAll(item => item is null); return objectArray; diff --git a/src/ElectronNET.API/Serialization/JsonToBoxedPrimitivesConverter.cs b/src/ElectronNET.API/Serialization/JsonToBoxedPrimitivesConverter.cs new file mode 100644 index 0000000..5db5304 --- /dev/null +++ b/src/ElectronNET.API/Serialization/JsonToBoxedPrimitivesConverter.cs @@ -0,0 +1,126 @@ +namespace ElectronNET.Serialization +{ + using System; + using System.Collections.Generic; + using System.Text.Json; + using System.Text.Json.Serialization; + + public sealed class JsonToBoxedPrimitivesConverter : JsonConverter + { + public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return ReadValue(ref reader); + } + + private static object ReadValue(ref Utf8JsonReader r) + { + switch (r.TokenType) + { + case JsonTokenType.StartObject: + + var obj = new Dictionary(); + while (r.Read()) + { + if (r.TokenType == JsonTokenType.EndObject) + { + return obj; + } + + if (r.TokenType != JsonTokenType.PropertyName) + { + throw new JsonException("Expected property name."); + } + + string name = r.GetString()!; + if (!r.Read()) + { + throw new JsonException("Unexpected end while reading property value."); + } + + obj[name] = ReadValue(ref r); + } + + throw new JsonException("Unexpected end while reading object."); + + case JsonTokenType.StartArray: + + var list = new List(); + while (r.Read()) + { + if (r.TokenType == JsonTokenType.EndArray) + { + return list; + } + + list.Add(ReadValue(ref r)); + } + + throw new JsonException("Unexpected end while reading array."); + + case JsonTokenType.True: return true; + case JsonTokenType.False: return false; + case JsonTokenType.Null: return null; + + case JsonTokenType.Number: + + if (r.TryGetInt32(out int i)) + { + return i; + } + + if (r.TryGetInt64(out long l)) + { + return l; + } + + if (r.TryGetDouble(out double d)) + { + return d; + } + + return r.GetDecimal(); + + case JsonTokenType.String: + + string s = r.GetString()!; + + if (DateTimeOffset.TryParse(s, out var dto)) + { + return dto; + } + + if (DateTime.TryParse(s, out var dt)) + { + return dt; + } + + if (TimeSpan.TryParse(s, out var ts)) + { + return ts; + } + + if (Guid.TryParse(s, out var g)) + { + return g; + } + + return s; + + default: + throw new JsonException($"Unsupported token {r.TokenType}"); + } + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + return; + } + + writer.WriteStartObject(); + writer.WriteEndObject(); + } + } +} \ No newline at end of file