From 0e8e96f2d83ddb74adc612a388c5459edeb74dc3 Mon Sep 17 00:00:00 2001 From: Florian Zevedei Date: Wed, 25 Sep 2024 16:56:03 +0200 Subject: [PATCH] Replacing Newtonsoft.Json with System.Text.Json (experimental) --- TelegramBotBase/Base/MessageResult.cs | 4 +- .../Interfaces/ISessionSerializationStage.cs | 5 +- TelegramBotBase/Form/CallbackData.cs | 14 +- .../Converter/DictionaryTypeConverter.cs | 155 ++++++++++++++++++ .../States/Converter/JsonTypeConverter.cs | 37 +++++ TelegramBotBase/States/JSONStateMachine.cs | 27 ++- .../States/SimpleJSONStateMachine.cs | 23 ++- TelegramBotBase/TelegramBotBase.csproj | 8 +- 8 files changed, 248 insertions(+), 25 deletions(-) create mode 100644 TelegramBotBase/States/Converter/DictionaryTypeConverter.cs create mode 100644 TelegramBotBase/States/Converter/JsonTypeConverter.cs diff --git a/TelegramBotBase/Base/MessageResult.cs b/TelegramBotBase/Base/MessageResult.cs index f1224c3..7cd2553 100644 --- a/TelegramBotBase/Base/MessageResult.cs +++ b/TelegramBotBase/Base/MessageResult.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Threading.Tasks; -using Newtonsoft.Json; using Telegram.Bot.Types; using Telegram.Bot.Types.Enums; @@ -126,7 +126,7 @@ public class MessageResult : ResultBase T cd = null; try { - cd = JsonConvert.DeserializeObject(RawData); + cd = JsonSerializer.Deserialize(RawData); return cd; } diff --git a/TelegramBotBase/Builder/Interfaces/ISessionSerializationStage.cs b/TelegramBotBase/Builder/Interfaces/ISessionSerializationStage.cs index 2a453e4..14400de 100644 --- a/TelegramBotBase/Builder/Interfaces/ISessionSerializationStage.cs +++ b/TelegramBotBase/Builder/Interfaces/ISessionSerializationStage.cs @@ -1,4 +1,5 @@ -using TelegramBotBase.Interfaces; +using System; +using TelegramBotBase.Interfaces; namespace TelegramBotBase.Builder.Interfaces; @@ -39,6 +40,7 @@ public interface ISessionSerializationStage /// /// /// + [Obsolete("Use UseJSON instead.")] ILanguageSelectionStage UseSimpleJSON(); /// @@ -46,6 +48,7 @@ public interface ISessionSerializationStage /// /// /// + [Obsolete("Use UseJSON instead.")] ILanguageSelectionStage UseSimpleJSON(string path); /// diff --git a/TelegramBotBase/Form/CallbackData.cs b/TelegramBotBase/Form/CallbackData.cs index 13b701a..e9143fe 100644 --- a/TelegramBotBase/Form/CallbackData.cs +++ b/TelegramBotBase/Form/CallbackData.cs @@ -1,16 +1,18 @@ -using Newtonsoft.Json; +using System.Text.Json; using System.Text; using TelegramBotBase.Exceptions; +using System.Text.Json.Serialization; namespace TelegramBotBase.Form; /// -/// Base class for serializing buttons and data +/// Base class for serializing buttons and data /// public class CallbackData { public CallbackData() { + } public CallbackData(string method, string value) @@ -19,9 +21,9 @@ public class CallbackData Value = value; } - [JsonProperty("m")] public string Method { get; set; } + [JsonPropertyName("m")] public string Method { get; set; } - [JsonProperty("v")] public string Value { get; set; } + [JsonPropertyName("v")] public string Value { get; set; } public static string Create(string method, string value) { @@ -36,7 +38,7 @@ public class CallbackData { var s = string.Empty; - s = JsonConvert.SerializeObject(this); + s = JsonSerializer.Serialize(this); //Is data over 64 bytes ? int byte_count = Encoding.UTF8.GetByteCount(s); @@ -55,7 +57,7 @@ public class CallbackData /// public static CallbackData Deserialize(string data) { - return JsonConvert.DeserializeObject(data); + return JsonSerializer.Deserialize(data); } public static implicit operator string(CallbackData callbackData) => callbackData.Serialize(true); diff --git a/TelegramBotBase/States/Converter/DictionaryTypeConverter.cs b/TelegramBotBase/States/Converter/DictionaryTypeConverter.cs new file mode 100644 index 0000000..c23cd8b --- /dev/null +++ b/TelegramBotBase/States/Converter/DictionaryTypeConverter.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace TelegramBotBase.States.Converter +{ + public class DictionaryObjectJsonConverter : JsonConverter> + { + public override Dictionary Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var dictionary = new Dictionary(); + var jsonDocument = JsonDocument.ParseValue(ref reader); + + foreach (var element in jsonDocument.RootElement.EnumerateObject()) + { + switch (element.Value.ValueKind) + { + case JsonValueKind.String: + dictionary[element.Name] = element.Value.GetString(); + break; + case JsonValueKind.Number: + + if (element.Value.TryGetInt32(out var number)) + { + dictionary[element.Name] = number; + continue; + } + + if (element.Value.TryGetInt64(out long l)) + dictionary[element.Name] = l; + else + dictionary[element.Name] = element.Value.GetDouble(); + break; + case JsonValueKind.True: + case JsonValueKind.False: + dictionary[element.Name] = element.Value.GetBoolean(); + break; + case JsonValueKind.Object: + dictionary[element.Name] = JsonSerializer.Deserialize>(element.Value.GetRawText(), options); + break; + case JsonValueKind.Array: + + dictionary[element.Name] = HandleArray(element.Value); + + break; + default: + dictionary[element.Name] = element.Value.GetRawText(); + break; + } + } + + return dictionary; + } + + + + private object HandleArray(JsonElement jsonArray) + { + // Hier wird geprüft, ob alle Elemente einer bestimmten Art angehören (z. B. int, string) + if (jsonArray.GetArrayLength() > 0) + { + var firstElement = jsonArray[0]; + switch (firstElement.ValueKind) + { + case JsonValueKind.Number: + // Prüfen, ob alle Elemente ganze Zahlen sind + var isIntArray = true; + var isLongArray = false; + + foreach (var element in jsonArray.EnumerateArray()) + { + if (!element.TryGetInt32(out _)) + { + isIntArray = false; + isLongArray = true; + if (!element.TryGetInt64(out _)) + { + isLongArray = false; + break; + } + } + } + if (isIntArray) + { + var list = new List(); + foreach (var element in jsonArray.EnumerateArray()) + { + list.Add(element.GetInt32()); + } + return list; + } + else if (isLongArray) + { + var list = new List(); + foreach (var element in jsonArray.EnumerateArray()) + { + list.Add(element.GetInt64()); + } + return list; + } + else + { + var list = new List(); + foreach (var element in jsonArray.EnumerateArray()) + { + list.Add(element.GetDouble()); + } + return list; + } + case JsonValueKind.String: + var stringList = new List(); + foreach (var element in jsonArray.EnumerateArray()) + { + stringList.Add(element.GetString()); + } + return stringList; + case JsonValueKind.True: + case JsonValueKind.False: + var boolList = new List(); + foreach (var element in jsonArray.EnumerateArray()) + { + boolList.Add(element.GetBoolean()); + } + return boolList; + default: + // Fallback: Liste von Objekten (z. B. wenn es sich um komplexe Objekte handelt) + var objectList = new List(); + foreach (var element in jsonArray.EnumerateArray()) + { + objectList.Add(JsonSerializer.Deserialize(element.GetRawText())); + } + return objectList; + } + } + + // Leeres Array als Liste von Objekten zurückgeben + return new List(); + } + + public override void Write(Utf8JsonWriter writer, Dictionary value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + foreach (var kvp in value) + { + writer.WritePropertyName(kvp.Key); + JsonSerializer.Serialize(writer, kvp.Value, kvp.Value?.GetType() ?? typeof(object), options); + } + + writer.WriteEndObject(); + } + } +} diff --git a/TelegramBotBase/States/Converter/JsonTypeConverter.cs b/TelegramBotBase/States/Converter/JsonTypeConverter.cs new file mode 100644 index 0000000..17517a9 --- /dev/null +++ b/TelegramBotBase/States/Converter/JsonTypeConverter.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace TelegramBotBase.States.Converter +{ + public class JsonTypeConverter : JsonConverter + { + + public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var jsonDocument = JsonDocument.ParseValue(ref reader); + var type = Type.GetType(jsonDocument.RootElement.GetProperty("$type").GetString()); + var jsonObject = jsonDocument.RootElement.GetRawText(); + return JsonSerializer.Deserialize(jsonObject, type, options); + } + + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + writer.WriteString("$type", value.GetType().AssemblyQualifiedName); + + var json = JsonSerializer.Serialize(value, value.GetType(), options); + using var jsonDoc = JsonDocument.Parse(json); + foreach (var property in jsonDoc.RootElement.EnumerateObject()) + { + property.WriteTo(writer); + } + + writer.WriteEndObject(); + } + + + } +} diff --git a/TelegramBotBase/States/JSONStateMachine.cs b/TelegramBotBase/States/JSONStateMachine.cs index 5c764a6..26dcffe 100644 --- a/TelegramBotBase/States/JSONStateMachine.cs +++ b/TelegramBotBase/States/JSONStateMachine.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using Newtonsoft.Json; +using System.Text.Json; using TelegramBotBase.Args; using TelegramBotBase.Base; using TelegramBotBase.Form; @@ -48,11 +48,15 @@ public class JsonStateMachine : IStateMachine { var content = File.ReadAllText(FilePath); - var sc = JsonConvert.DeserializeObject(content, new JsonSerializerSettings + var options = new JsonSerializerOptions { - TypeNameHandling = TypeNameHandling.All, - TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple - }); + Converters = { + new Converter.DictionaryObjectJsonConverter(), + new Converter.JsonTypeConverter() + } + }; + + var sc = JsonSerializer.Deserialize(content, options); return sc; } @@ -77,11 +81,16 @@ public class JsonStateMachine : IStateMachine try { - var content = JsonConvert.SerializeObject(e.States, Formatting.Indented, new JsonSerializerSettings + var options = new JsonSerializerOptions { - TypeNameHandling = TypeNameHandling.All, - TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple - }); + WriteIndented = true, + Converters = { + new Converter.DictionaryObjectJsonConverter(), + new Converter.JsonTypeConverter() + } + }; + + var content = JsonSerializer.Serialize(e.States, options); File.WriteAllText(FilePath, content); } diff --git a/TelegramBotBase/States/SimpleJSONStateMachine.cs b/TelegramBotBase/States/SimpleJSONStateMachine.cs index e722305..619abce 100644 --- a/TelegramBotBase/States/SimpleJSONStateMachine.cs +++ b/TelegramBotBase/States/SimpleJSONStateMachine.cs @@ -1,6 +1,6 @@ using System; using System.IO; -using Newtonsoft.Json; +using System.Text.Json; using TelegramBotBase.Args; using TelegramBotBase.Base; using TelegramBotBase.Form; @@ -49,7 +49,15 @@ public class SimpleJsonStateMachine : IStateMachine { var content = File.ReadAllText(FilePath); - var sc = JsonConvert.DeserializeObject(content); + var options = new JsonSerializerOptions + { + Converters = { + new Converter.DictionaryObjectJsonConverter(), + new Converter.JsonTypeConverter() + } + }; + + var sc = JsonSerializer.Deserialize(content, options); return sc; } @@ -74,7 +82,16 @@ public class SimpleJsonStateMachine : IStateMachine try { - var content = JsonConvert.SerializeObject(e.States, Formatting.Indented); + var options = new JsonSerializerOptions + { + WriteIndented = true, + Converters = { + new Converter.DictionaryObjectJsonConverter(), + new Converter.JsonTypeConverter() + } + }; + + var content = JsonSerializer.Serialize(e.States, options); File.WriteAllText(FilePath, content); } diff --git a/TelegramBotBase/TelegramBotBase.csproj b/TelegramBotBase/TelegramBotBase.csproj index 90b1068..66bd60c 100644 --- a/TelegramBotBase/TelegramBotBase.csproj +++ b/TelegramBotBase/TelegramBotBase.csproj @@ -1,7 +1,7 @@  - netstandard2.0;netcoreapp3.1;net6;net7;net8 + netcoreapp3.1;net6;net7;net8 10 false False @@ -22,8 +22,8 @@ - - + + @@ -62,7 +62,7 @@ - +