瀏覽代碼

Implemented default object serializer converter to avoid deserialising into JSON objects

Kenric Nugteren 3 周之前
父節點
當前提交
d38783ec20
共有 1 個文件被更改,包括 89 次插入0 次删除
  1. 89 0
      InABox.Core/Serialization.cs

+ 89 - 0
InABox.Core/Serialization.cs

@@ -57,6 +57,8 @@ namespace InABox.Core
             new MultiQueryRequestConverter(),
             new UserPropertiesJsonConverter(),
             new TypeJsonConverter(),
+            new PolymorphicConverter(),
+            new ObjectConverter(), // Our fallback, which converts JSON objects into real ones.
         };
 
         private static JsonSerializerOptions SerializerSettings(bool indented = true, bool populateObject = false)
@@ -885,6 +887,9 @@ namespace InABox.Core
         }
     }
 
+    /// <summary>
+    /// When serialising an object implementing this interface, a '$type' field will be added.
+    /// </summary>
     public interface IPolymorphicallySerialisable { }
 
     /// <summary>
@@ -974,6 +979,20 @@ namespace InABox.Core
             }
         }
 
+        protected delegate void ArrayValueHandler(ref Utf8JsonReader reader);
+
+        protected void ReadArray(ref Utf8JsonReader reader, ArrayValueHandler onValue)
+        {
+            if (reader.TokenType != JsonTokenType.StartArray)
+            {
+                throw new JsonException();
+            }
+            while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
+            {
+                onValue(ref reader);
+            }
+        }
+
         protected delegate void ObjectPropertyHandler(ref Utf8JsonReader reader, string propertyName);
 
         protected void ReadObject(ref Utf8JsonReader reader, ObjectPropertyHandler onProperty)
@@ -1060,6 +1079,76 @@ namespace InABox.Core
         }
     }
 
+    public class ObjectConverter : CustomJsonConverter<object?>
+    {
+        public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+        {
+            switch (reader.TokenType)
+            {
+                case JsonTokenType.StartObject:
+                    var dict = new Dictionary<string, object?>();
+                    ReadObject(ref reader, (ref Utf8JsonReader reader, string property) =>
+                    {
+                        dict[property] = Read(ref reader, typeof(object), options);
+                    });
+                    return dict;
+                case JsonTokenType.StartArray:
+                    var list = new List<object?>();
+                    ReadArray(ref reader, (ref Utf8JsonReader reader) =>
+                    {
+                        list.Add(Read(ref reader, typeof(object), options));
+                    });
+                    return list.ToArray();
+                case JsonTokenType.String:
+                    return reader.GetString();
+                case JsonTokenType.False:
+                    return false;
+                case JsonTokenType.True:
+                    return true;
+                case JsonTokenType.Number:
+                    if(reader.TryGetInt32(out var iValue))
+                    {
+                        return iValue;
+                    }
+                    else if(reader.TryGetInt64(out var lValue))
+                    {
+                        return lValue;
+                    }
+                    else if(reader.TryGetDouble(out var dValue))
+                    {
+                        return dValue;
+                    }
+                    else
+                    {
+                        return null;
+                    }
+                case JsonTokenType.Null:
+                    return null;
+                default:
+                    throw new JsonException();
+            }
+        }
+
+        public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
+        {
+            if(value is null)
+            {
+                writer.WriteNullValue();
+            }
+            else if(value.GetType() == typeof(object))
+            {
+                writer.WriteStartObject();
+                writer.WriteEndObject();
+            }
+            else
+            {
+                // Call the serialiser, but this time with the value's real type, so this particular won't get called (since this only
+                // gets called if the type passed into 'Serialize' is *identically* 'object'.
+                JsonSerializer.Serialize(writer, value, value.GetType(), options);
+            }
+        }
+    }
+
     public class TypeJsonConverter : CustomJsonConverter<Type>
     {
         public override Type? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)