Code/Any.cs
using Sandbox;
using System;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ExtendedEditor;
[JsonConverter(typeof(Any.AnyJsonConverterFactory))]
public struct Any<T>
{
public T Value { get; set; }
public Any() : this(default!)
{
}
public Any(T value)
{
Value = value;
}
}
public static class Any
{
public class AnyJsonConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) => typeToConvert.IsGenericType && typeof(Any<>) == TypeLibrary.GetType(typeToConvert).TargetType;
public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
var type = TypeLibrary.GetType(typeof(AnyJsonConverter<>)).MakeGenericType(TypeLibrary.GetGenericArguments(typeToConvert));
return TypeLibrary.Create<JsonConverter>(type);
}
}
public class AnyJsonConverter<T> : JsonConverter<Any<T>>
{
[SkipHotload]
private static JsonSerializerOptions? _typeConverterOptions;
public override Any<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if(reader.TokenType == JsonTokenType.Null)
{
if(typeToConvert.IsValueType && TypeLibrary.GetType(typeToConvert).TargetType != typeof(Nullable<>))
Log.Warning($"Can't convert {JsonTokenType.Null} to {typeToConvert.FullName}");
return default;
}
Type? type = null;
JsonDocument? valueNode = null;
try
{
if(reader.TokenType != JsonTokenType.StartObject)
throw new InvalidOperationException($"Token {reader.TokenType} is not expected. Expected {JsonTokenType.StartObject}.");
while(reader.Read() && reader.TokenType != JsonTokenType.EndObject)
{
if(reader.TokenType != JsonTokenType.PropertyName)
throw new InvalidOperationException($"Token {reader.TokenType} is not expected. Expected {JsonTokenType.PropertyName}.");
var propertyName = reader.ValueSpan;
if(propertyName.SequenceEqual("$type"u8))
{
if(_typeConverterOptions is null)
{
_typeConverterOptions = new(options);
_typeConverterOptions.Converters.Clear();
_typeConverterOptions.Converters.Add(new TypeJsonConverter());
}
reader.Read();
type = JsonSerializer.Deserialize<Type>(ref reader, _typeConverterOptions);
}
else if(propertyName.SequenceEqual("$value"u8))
{
reader.Read();
valueNode = JsonDocument.ParseValue(ref reader);
}
else
{
reader.Skip();
}
}
if(reader.TokenType != JsonTokenType.EndObject)
throw new InvalidOperationException($"Token {reader.TokenType} is not expected. Expected {JsonTokenType.EndObject}.");
if(type is null)
{
Log.Warning($"Type not found by converter {typeof(JsonConverter).FullName} when converting value.");
return default;
}
if(valueNode is null)
{
Log.Warning($"Value node not found by converter {typeof(JsonConverter).FullName} when converting value.");
return default;
}
var value = JsonSerializer.Deserialize(valueNode, type, options);
if(value is not T t)
{
Log.Warning($"Value {value} is not of type {typeof(T)}.");
return default;
}
return new Any<T>(t);
}
finally
{
valueNode?.Dispose();
}
}
public override void Write(Utf8JsonWriter writer, Any<T> obj, JsonSerializerOptions options)
{
var value = obj.Value;
if(value is null)
{
writer.WriteNullValue();
return;
}
if(_typeConverterOptions is null)
{
_typeConverterOptions = new(options);
_typeConverterOptions.Converters.Clear();
_typeConverterOptions.Converters.Add(new TypeJsonConverter());
}
writer.WriteStartObject();
writer.WritePropertyName("$type");
JsonSerializer.Serialize(writer, value.GetType(), _typeConverterOptions);
writer.WritePropertyName("$value");
JsonSerializer.Serialize(writer, value, value.GetType(), options);
writer.WriteEndObject();
}
}
}