Updating json serializer to fit System.Text.Json

This commit is contained in:
Florian Zevedei 2024-11-09 16:04:42 +01:00
parent 19d8a23b98
commit 667ca5ba6c
2 changed files with 289 additions and 24 deletions

View File

@ -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<string, object>();
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<string, object>();
// 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<string, object>();
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<object>(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<object>
var list = new List<object>();
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();
}
}
}
}

View File

@ -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<StateContainer>(content, options);
var obj = JsonSerializer.Deserialize<object>(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<object>(e.States, options);
File.WriteAllText(FilePath, content);
}
catch
{
}
}
}