TypeJsonConverter.cs
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ExtendedEditor;
public class TypeJsonConverter : JsonConverter<Type>
{
public const string TypePropertyName = "$type";
public const string GenericArgsPropertyName = "$args";
public override Type? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if(typeToConvert != typeof(Type))
{
Log.Warning($"Can't deserialize Type to {typeToConvert.FullName}.");
return null!;
}
if(reader.TokenType == JsonTokenType.Null)
return null!;
if(reader.TokenType != JsonTokenType.String && reader.TokenType != JsonTokenType.StartObject)
{
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Expected one of [{JsonTokenType.Null}, {JsonTokenType.String}, {JsonTokenType.StartObject}].");
return default!;
}
if(reader.TokenType == JsonTokenType.String)
{
var type = TypeLibrary.GetType(reader.GetString());
if(type is null)
{
Log.Warning($"Type {reader.GetString()} is not allowed to be deserialized by whitelist.");
return null!;
}
return type.TargetType;
}
TypeDescription? genericTypeDefinition = null;
List<Type?> args = [];
while(reader.Read() && reader.TokenType != JsonTokenType.EndObject)
{
if(reader.TokenType != JsonTokenType.PropertyName)
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Expected {JsonTokenType.PropertyName}.");
var name = reader.ValueSpan;
if(name.SequenceEqual("$type"u8))
{
if(!reader.Read() || (reader.TokenType != JsonTokenType.String))
{
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Expected {JsonTokenType.String}.");
return null!;
}
var type = TypeLibrary.GetType(reader.GetString());
if(type is null)
{
Log.Warning($"Type {reader.GetString()} is not allowed to be deserialized by whitelist.");
return null!;
}
genericTypeDefinition = type;
}
else if(name.SequenceEqual("$args"u8))
{
if(!reader.Read() || reader.TokenType != JsonTokenType.StartArray)
{
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Expected {JsonTokenType.StartArray}.");
return null!;
}
while(reader.Read() && reader.TokenType != JsonTokenType.EndArray)
{
var argType = Read(ref reader, typeof(Type), options);
args.Add(argType);
}
if(reader.TokenType != JsonTokenType.EndArray)
{
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Expected {JsonTokenType.EndArray}.");
return null;
}
}
else
{
reader.Skip();
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Unknown property '{reader.GetString()}' in {nameof(TypeJsonConverter)}.");
}
}
if(reader.TokenType != JsonTokenType.EndObject)
{
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Expected {JsonTokenType.EndObject}.");
return null;
}
if(genericTypeDefinition is null)
{
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Generic type definition not found or not generic type definition.");
return null;
}
if(args.Count == 0)
{
Log.Warning($"Invalid JSON format for {nameof(TypeJsonConverter)}. Generic arguments not found.");
return null;
}
if(args.Any(x => x is null))
return genericTypeDefinition.TargetType;
var result = genericTypeDefinition.MakeGenericType([.. args]);
if(result is null)
Log.Warning($"Type {genericTypeDefinition.FullName} is not allowed to be deserialized by constraints or by whitelist with theese generic arguments: {string.Join(", ", args.Select(x => x?.FullName ?? "null"))}.");
return result;
}
public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options)
{
if(value is null)
{
writer.WriteNullValue();
return;
}
if(value.IsGenericType)
{
var typeDescription = TypeLibrary.GetType(value);
if(typeDescription is null)
{
Log.Warning($"Type {value} is not allowed to be serialized by whitelist.");
return;
}
var genericTypeDefinition = typeDescription.TargetType;
var isGenericTypeDefinition = genericTypeDefinition == value;
if(isGenericTypeDefinition)
{
writer.WriteStringValue(genericTypeDefinition.FullName);
}
else
{
writer.WriteStartObject();
writer.WriteString(TypePropertyName, genericTypeDefinition.FullName);
writer.WritePropertyName(GenericArgsPropertyName);
writer.WriteStartArray();
foreach(var parameterType in TypeLibrary.GetGenericArguments(value))
Write(writer, parameterType, options);
writer.WriteEndArray();
writer.WriteEndObject();
}
}
else
{
writer.WriteStringValue(value.FullName);
}
}
}