|
@@ -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)
|