Replacing Newtonsoft.Json with System.Text.Json (experimental)

This commit is contained in:
Florian Zevedei 2024-09-25 16:56:03 +02:00
parent f21dec5f1c
commit 0e8e96f2d8
8 changed files with 248 additions and 25 deletions

View File

@ -1,8 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json;
using Telegram.Bot.Types; using Telegram.Bot.Types;
using Telegram.Bot.Types.Enums; using Telegram.Bot.Types.Enums;
@ -126,7 +126,7 @@ public class MessageResult : ResultBase
T cd = null; T cd = null;
try try
{ {
cd = JsonConvert.DeserializeObject<T>(RawData); cd = JsonSerializer.Deserialize<T>(RawData);
return cd; return cd;
} }

View File

@ -1,4 +1,5 @@
using TelegramBotBase.Interfaces; using System;
using TelegramBotBase.Interfaces;
namespace TelegramBotBase.Builder.Interfaces; namespace TelegramBotBase.Builder.Interfaces;
@ -39,6 +40,7 @@ public interface ISessionSerializationStage
/// </summary> /// </summary>
/// <param name="path"></param> /// <param name="path"></param>
/// <returns></returns> /// <returns></returns>
[Obsolete("Use UseJSON instead.")]
ILanguageSelectionStage UseSimpleJSON(); ILanguageSelectionStage UseSimpleJSON();
/// <summary> /// <summary>
@ -46,6 +48,7 @@ public interface ISessionSerializationStage
/// </summary> /// </summary>
/// <param name="path"></param> /// <param name="path"></param>
/// <returns></returns> /// <returns></returns>
[Obsolete("Use UseJSON instead.")]
ILanguageSelectionStage UseSimpleJSON(string path); ILanguageSelectionStage UseSimpleJSON(string path);
/// <summary> /// <summary>

View File

@ -1,6 +1,7 @@
using Newtonsoft.Json; using System.Text.Json;
using System.Text; using System.Text;
using TelegramBotBase.Exceptions; using TelegramBotBase.Exceptions;
using System.Text.Json.Serialization;
namespace TelegramBotBase.Form; namespace TelegramBotBase.Form;
@ -11,6 +12,7 @@ public class CallbackData
{ {
public CallbackData() public CallbackData()
{ {
} }
public CallbackData(string method, string value) public CallbackData(string method, string value)
@ -19,9 +21,9 @@ public class CallbackData
Value = value; 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) public static string Create(string method, string value)
{ {
@ -36,7 +38,7 @@ public class CallbackData
{ {
var s = string.Empty; var s = string.Empty;
s = JsonConvert.SerializeObject(this); s = JsonSerializer.Serialize(this);
//Is data over 64 bytes ? //Is data over 64 bytes ?
int byte_count = Encoding.UTF8.GetByteCount(s); int byte_count = Encoding.UTF8.GetByteCount(s);
@ -55,7 +57,7 @@ public class CallbackData
/// <returns></returns> /// <returns></returns>
public static CallbackData Deserialize(string data) public static CallbackData Deserialize(string data)
{ {
return JsonConvert.DeserializeObject<CallbackData>(data); return JsonSerializer.Deserialize<CallbackData>(data);
} }
public static implicit operator string(CallbackData callbackData) => callbackData.Serialize(true); public static implicit operator string(CallbackData callbackData) => callbackData.Serialize(true);

View File

@ -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<Dictionary<string, object>>
{
public override Dictionary<string, object> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var dictionary = new Dictionary<string, object>();
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<Dictionary<string, object>>(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<int>();
foreach (var element in jsonArray.EnumerateArray())
{
list.Add(element.GetInt32());
}
return list;
}
else if (isLongArray)
{
var list = new List<long>();
foreach (var element in jsonArray.EnumerateArray())
{
list.Add(element.GetInt64());
}
return list;
}
else
{
var list = new List<double>();
foreach (var element in jsonArray.EnumerateArray())
{
list.Add(element.GetDouble());
}
return list;
}
case JsonValueKind.String:
var stringList = new List<string>();
foreach (var element in jsonArray.EnumerateArray())
{
stringList.Add(element.GetString());
}
return stringList;
case JsonValueKind.True:
case JsonValueKind.False:
var boolList = new List<bool>();
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<object>();
foreach (var element in jsonArray.EnumerateArray())
{
objectList.Add(JsonSerializer.Deserialize<object>(element.GetRawText()));
}
return objectList;
}
}
// Leeres Array als Liste von Objekten zurückgeben
return new List<object>();
}
public override void Write(Utf8JsonWriter writer, Dictionary<string, object> 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();
}
}
}

View File

@ -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<object>
{
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();
}
}
}

View File

@ -1,6 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using Newtonsoft.Json; using System.Text.Json;
using TelegramBotBase.Args; using TelegramBotBase.Args;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form; using TelegramBotBase.Form;
@ -48,11 +48,15 @@ public class JsonStateMachine : IStateMachine
{ {
var content = File.ReadAllText(FilePath); var content = File.ReadAllText(FilePath);
var sc = JsonConvert.DeserializeObject<StateContainer>(content, new JsonSerializerSettings var options = new JsonSerializerOptions
{ {
TypeNameHandling = TypeNameHandling.All, Converters = {
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple new Converter.DictionaryObjectJsonConverter(),
}); new Converter.JsonTypeConverter()
}
};
var sc = JsonSerializer.Deserialize<StateContainer>(content, options);
return sc; return sc;
} }
@ -77,11 +81,16 @@ public class JsonStateMachine : IStateMachine
try try
{ {
var content = JsonConvert.SerializeObject(e.States, Formatting.Indented, new JsonSerializerSettings var options = new JsonSerializerOptions
{ {
TypeNameHandling = TypeNameHandling.All, WriteIndented = true,
TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple Converters = {
}); new Converter.DictionaryObjectJsonConverter(),
new Converter.JsonTypeConverter()
}
};
var content = JsonSerializer.Serialize(e.States, options);
File.WriteAllText(FilePath, content); File.WriteAllText(FilePath, content);
} }

View File

@ -1,6 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using Newtonsoft.Json; using System.Text.Json;
using TelegramBotBase.Args; using TelegramBotBase.Args;
using TelegramBotBase.Base; using TelegramBotBase.Base;
using TelegramBotBase.Form; using TelegramBotBase.Form;
@ -49,7 +49,15 @@ public class SimpleJsonStateMachine : IStateMachine
{ {
var content = File.ReadAllText(FilePath); var content = File.ReadAllText(FilePath);
var sc = JsonConvert.DeserializeObject<StateContainer>(content); var options = new JsonSerializerOptions
{
Converters = {
new Converter.DictionaryObjectJsonConverter(),
new Converter.JsonTypeConverter()
}
};
var sc = JsonSerializer.Deserialize<StateContainer>(content, options);
return sc; return sc;
} }
@ -74,7 +82,16 @@ public class SimpleJsonStateMachine : IStateMachine
try 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); File.WriteAllText(FilePath, content);
} }

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netstandard2.0;netcoreapp3.1;net6;net7;net8</TargetFrameworks> <TargetFrameworks>netcoreapp3.1;net6;net7;net8</TargetFrameworks>
<LangVersion>10</LangVersion> <LangVersion>10</LangVersion>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild> <GeneratePackageOnBuild>False</GeneratePackageOnBuild>
@ -22,8 +22,8 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
</ItemGroup> </ItemGroup>