From d3ad4789a1739b4fff6a588033eea0ca9a2ba76c Mon Sep 17 00:00:00 2001 From: Wizou <11647984+wiz0u@users.noreply.github.com> Date: Fri, 5 Dec 2025 06:53:54 +0100 Subject: [PATCH] WTelegram.Helpers.JsonOptions can now serialize polymorph TL types (useful for logs). Deserialization is also possible in non-trimmed apps, but not recommended as structures can change. --- src/Helpers.cs | 82 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/Helpers.cs b/src/Helpers.cs index d483cb6..ea7d0b5 100644 --- a/src/Helpers.cs +++ b/src/Helpers.cs @@ -27,8 +27,88 @@ namespace WTelegram public static readonly JsonSerializerOptions JsonOptions = new() { IncludeFields = true, WriteIndented = true, #if NET8_0_OR_GREATER TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault ? null : WTelegramContext.Default, + Converters = { new TLJsonConverter(), new JsonStringEnumConverter() }, +#endif + IgnoreReadOnlyProperties = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault }; + +#if NET8_0_OR_GREATER + public sealed class TLJsonConverter : JsonConverter + { + public override bool CanConvert(Type typeToConvert) + => typeToConvert.IsAbstract || typeToConvert == typeof(Dictionary) || typeToConvert == typeof(Dictionary); + public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (typeToConvert == typeof(Dictionary)) + { + if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException("Expected array for users dictionary"); + var users = new Dictionary(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + var user = JsonSerializer.Deserialize(ref reader, options); + if (user != null) users[user.id] = user; + } + return users; + } + else if (typeToConvert == typeof(Dictionary)) + { + if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException("Expected array for chats dictionary"); + var chats = new Dictionary(); + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + var chat = (TL.ChatBase)Read(ref reader, typeof(TL.ChatBase), options); + if (chat != null) chats[chat.ID] = chat; + } + return chats; + } + else if (reader.TokenType == JsonTokenType.Null) + return null; + else if (reader.TokenType == JsonTokenType.StartObject) + { + var typeReader = reader; + if (!typeReader.Read() || typeReader.TokenType != JsonTokenType.PropertyName || typeReader.GetString() != "$") + throw new JsonException("Expected $ type property"); + if (!typeReader.Read() || typeReader.TokenType != JsonTokenType.String) + throw new JsonException("Invalid $ type property"); + var type = typeReader.GetString(); + var actualType = typeToConvert.Assembly.GetType("TL." + type); + if (!typeToConvert.IsAssignableFrom(actualType)) + throw new JsonException($"Incompatible $ type: {type} -> {typeToConvert}"); + return JsonSerializer.Deserialize(ref reader, actualType, options); + } + throw new JsonException($"Unexpected token type: {reader.TokenType}"); + } + public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + if (value is Dictionary users) + { + writer.WriteStartArray(); + foreach (var element in users.Values) + JsonSerializer.Serialize(writer, element, options); + writer.WriteEndArray(); + } + else if (value is Dictionary chats) + { + writer.WriteStartArray(); + foreach (var element in chats.Values) + Write(writer, element, options); + writer.WriteEndArray(); + } + else if (value is null) + writer.WriteNullValue(); + else + { + var actualType = value.GetType(); + var jsonObject = JsonSerializer.SerializeToElement(value, actualType, options); + writer.WriteStartObject(); + writer.WriteString("$", actualType.Name); + foreach (var property in jsonObject.EnumerateObject()) + if (char.IsLower(property.Name[0])) + property.WriteTo(writer); + writer.WriteEndObject(); + } + } + } #endif - IgnoreReadOnlyProperties = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; private static readonly ConsoleColor[] LogLevelToColor = [ ConsoleColor.DarkGray, ConsoleColor.DarkCyan, ConsoleColor.Cyan, ConsoleColor.Yellow, ConsoleColor.Red, ConsoleColor.Magenta, ConsoleColor.DarkBlue ];