diff --git a/TelegramBotBase/States/Converter/JsonTypeConverter.cs b/TelegramBotBase/States/Converter/JsonTypeConverter.cs index 17517a9..ac63fd0 100644 --- a/TelegramBotBase/States/Converter/JsonTypeConverter.cs +++ b/TelegramBotBase/States/Converter/JsonTypeConverter.cs @@ -1,5 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -11,27 +14,283 @@ namespace TelegramBotBase.States.Converter 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); + using (JsonDocument doc = JsonDocument.ParseValue(ref reader)) + { + return ParseElement(doc.RootElement, options, typeof(object)); + } + + } + + private object ParseElement(JsonElement element, JsonSerializerOptions options, Type expected_type) + { + // Wenn das Element ein JSON-Objekt ist + if (element.ValueKind == JsonValueKind.Object) + { + // Wenn es einen $type enthält, dann den Typ dynamisch instanziieren + if (element.TryGetProperty("$type", out JsonElement typeElement)) + { + string typeName = typeElement.GetString(); + Type type = Type.GetType(typeName); + + if (type == null) + throw new InvalidOperationException($"Typ '{typeName}' konnte nicht gefunden werden."); + + // Liste verarbeiten, falls $values enthalten ist + if (element.TryGetProperty("$values", out JsonElement valuesElement)) + { + return ParseArray(valuesElement, type); + } + else if (expected_type.IsGenericType && expected_type.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + { + var dictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + dictionary[property.Name] = ParseElement(property.Value, options, typeof(object)); + } + + return dictionary; + } + else + { + var instance = type.GetConstructor(new Type[] { }).Invoke(new object[] { }); + var properties = type.GetProperties(); + + foreach (var p in properties) + { + if (!element.TryGetProperty(p.Name, out JsonElement value)) + continue; + + switch (value.ValueKind) + { + case JsonValueKind.Number: + + if (p.PropertyType == typeof(int)) + { + if (value.TryGetInt32(out int i)) + { + p.SetValue(instance, i); + } + } + else if (p.PropertyType == typeof(long)) + { + if (value.TryGetInt64(out long l)) + { + p.SetValue(instance, l); + } + + } + + break; + case JsonValueKind.String: + + p.SetValue(instance, value.ToString()); + + break; + + default: + + //if (p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + //{ + // var dictionary = new Dictionary(); + // foreach (var property in element.EnumerateObject()) + // { + // dictionary[property.Name] = ParseElement(property.Value, options, p.PropertyType); + // } + + // p.SetValue(instance, dictionary); + //} + //else + //{ + + var obj = ParseElement(value, options, p.PropertyType); + + if (obj == null) + continue; + + try + { + p.SetValue(instance, obj); + } + catch + { + + } + //} + + + break; + } + + + + } + + return instance; + + // Objekt dynamisch deserialisieren + //return JsonSerializer.Deserialize(element.GetRawText(), type, options); + } + } + else + { + // Falls kein $type vorhanden ist, als Dictionary verarbeiten + var dictionary = new Dictionary(); + foreach (var property in element.EnumerateObject()) + { + dictionary[property.Name] = ParseElement(property.Value, options, typeof(object)); + } + return dictionary; + } + } + // Wenn das Element ein Array ist + else if (element.ValueKind == JsonValueKind.Array) + { + return ParseArray(element, typeof(object)); + } + // Primitive Typen (String, Number, Bool) + else if (element.ValueKind == JsonValueKind.Number) + { + + if (expected_type == typeof(Int64)) + { + if (element.TryGetInt64(out long l)) + return l; + } + else if (expected_type == typeof(Int32)) + { + if (element.TryGetInt32(out int i)) + return i; + } + + if (element.TryGetInt32(out int i2)) + return i2; + + return 0; + } + else if (element.ValueKind == JsonValueKind.String) + { + return element.ToString(); + } + else + { + +#if NETCOREAPP3_1 + return JsonSerializer.Deserialize(element.ToString(), typeof(object), options); +#else + return element.Deserialize(options); +#endif + + } + } + + private object ParseElement(JsonElement element) + { + + return null; + } + + private object ParseArray(JsonElement arrayElement, Type listType) + { + // Wenn der Typ eine generische Liste ist + if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(List<>)) + { + Type elementType = listType.GetGenericArguments()[0]; + var list = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(elementType)); + foreach (var item in arrayElement.EnumerateArray()) + { + list.Add(ParseElement(item, new JsonSerializerOptions(), elementType)); + } + return list; + } + else + { + // Standardmäßig als List + var list = new List(); + foreach (var item in arrayElement.EnumerateArray()) + { + list.Add(ParseElement(item, new JsonSerializerOptions(), typeof(object))); + } + return list; + } } public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { - writer.WriteStartObject(); - writer.WriteString("$type", value.GetType().AssemblyQualifiedName); + WriteWithTypeInfo(writer, value, options); - 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(); } + private void WriteWithTypeInfo(Utf8JsonWriter writer, object value, JsonSerializerOptions options) + { + if (value == null) + { + writer.WriteNullValue(); + return; + } + Type type = value.GetType(); + + // Primitives und grundlegende Typen (int, string, bool, etc.) + if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal)) + { + JsonSerializer.Serialize(writer, value, type, options); + } + // Behandlung für Sammlungen + else if (value is IList list) + { + writer.WriteStartObject(); + writer.WriteString("$type", type.AssemblyQualifiedName); + writer.WritePropertyName("$values"); + writer.WriteStartArray(); + + foreach (var item in list) + { + WriteWithTypeInfo(writer, item, options); + } + + writer.WriteEndArray(); + writer.WriteEndObject(); + } + // Behandlung für Dictionaries + else if (value is IDictionary dictionary) + { + writer.WriteStartObject(); + writer.WriteString("$type", type.AssemblyQualifiedName); + + foreach (DictionaryEntry entry in dictionary) + { + writer.WritePropertyName(entry.Key.ToString()); + WriteWithTypeInfo(writer, entry.Value, options); + } + + writer.WriteEndObject(); + } + // Komplexe Objekte + else if (value is Enum e) + { + + int eValue = (int)value; + + writer.WriteNumberValue(eValue); + + } + else + { + writer.WriteStartObject(); + writer.WriteString("$type", type.AssemblyQualifiedName); + + foreach (var property in type.GetProperties()) + { + if (property.CanRead) + { + var propValue = property.GetValue(value); + writer.WritePropertyName(property.Name); + WriteWithTypeInfo(writer, propValue, options); + } + } + + writer.WriteEndObject(); + } + } } } diff --git a/TelegramBotBase/States/JSONStateMachine.cs b/TelegramBotBase/States/JSONStateMachine.cs index 26dcffe..ed0c64c 100644 --- a/TelegramBotBase/States/JSONStateMachine.cs +++ b/TelegramBotBase/States/JSONStateMachine.cs @@ -50,18 +50,22 @@ public class JsonStateMachine : IStateMachine var options = new JsonSerializerOptions { - Converters = { - new Converter.DictionaryObjectJsonConverter(), - new Converter.JsonTypeConverter() - } + Converters = { + new Converter.JsonTypeConverter(), + //new Converter.DictionaryObjectJsonConverter(), + + }, + PropertyNameCaseInsensitive = true }; - var sc = JsonSerializer.Deserialize(content, options); + var obj = JsonSerializer.Deserialize(content, options) ; + var sc = obj as StateContainer; return sc; } - catch + catch(Exception ex) { + throw ex; } return new StateContainer(); @@ -85,17 +89,19 @@ public class JsonStateMachine : IStateMachine { WriteIndented = true, Converters = { - new Converter.DictionaryObjectJsonConverter(), - new Converter.JsonTypeConverter() - } + new Converter.JsonTypeConverter(), + }, + PropertyNameCaseInsensitive = true }; - var content = JsonSerializer.Serialize(e.States, options); + var content = JsonSerializer.Serialize(e.States, options); File.WriteAllText(FilePath, content); } catch { + + } } } \ No newline at end of file