2024-03-25 16:14:17 +00:00

268 lines
8.8 KiB
C#

using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Numerics;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using DynamicBible.DataPreparation.Models;
namespace DynamicBible.DataPreparation;
public class JSON
{
static JSON()
{
jsonOptions = new ()
{
AllowTrailingCommas = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
IgnoreReadOnlyProperties = false,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
};
jsonOptions.Converters.Add(DictionaryJsonConverterFactory.Default);
}
private static readonly JsonSerializerOptions jsonOptions;
public static string Serialize<T>(T obj)
{
return JsonSerializer.Serialize(obj, jsonOptions);
}
public static T? Deserialize<T>(string obj)
{
return JsonSerializer.Deserialize<T>(obj, jsonOptions);
}
}
public class DictionaryJsonConverterFactoryBuilder
{
private readonly Dictionary<Type, Delegate> _parsers = new ();
private readonly Dictionary<Type, Delegate> _serializers = new ();
public DictionaryJsonConverterFactoryBuilder AddParser<T>(Converter<string, T> parser)
{
_parsers.Add(typeof(T), parser);
return this;
}
public DictionaryJsonConverterFactoryBuilder AddSerializer<T>(Converter<T, string> serializer)
{
_serializers.Add(typeof(T), serializer);
return this;
}
public DictionaryJsonConverterFactoryBuilder Add<T>(
Converter<string, T> parser, Converter<T, string> serializer)
{
return AddParser(parser).AddSerializer(serializer);
}
public DictionaryJsonConverterFactoryBuilder SetParser<T>(Converter<string, T> parser)
{
_parsers[typeof(T)] = parser;
return this;
}
public DictionaryJsonConverterFactoryBuilder SetSerializer<T>(Converter<T, string> serializer)
{
_serializers[typeof(T)] = serializer;
return this;
}
public DictionaryJsonConverterFactoryBuilder Set<T>(
Converter<string, T> parser, Converter<T, string> serializer)
{
return SetParser(parser).SetSerializer(serializer);
}
public DictionaryJsonConverterFactoryBuilder AddDefaults()
{
return this
.AddParser(sbyte.Parse)
.AddParser(short.Parse)
.AddParser(int.Parse)
.AddParser(long.Parse)
.AddParser(byte.Parse)
.AddParser(ushort.Parse)
.AddParser(uint.Parse)
.AddParser(ulong.Parse)
.AddParser(BigInteger.Parse)
.AddParser(float.Parse)
.AddParser(double.Parse)
.AddParser(decimal.Parse)
.AddParser(Guid.Parse);
}
public DictionaryJsonConverterFactory Build()
{
return new DictionaryJsonConverterFactory(
_parsers.ToImmutableDictionary(),
_serializers.ToImmutableDictionary());
}
}
public sealed class DictionaryJsonConverterFactory : JsonConverterFactory
{
private static readonly ImmutableDictionary<Type, Type> s_converterTypes = new Dictionary<Type, Type>
{
[typeof(Dictionary<,>)] = typeof(DictionaryJsonConverter<,>),
}.ToImmutableDictionary();
public static readonly ImmutableArray<Type> SupportedDictionaryTypes = s_converterTypes.Keys.ToImmutableArray();
public static readonly DictionaryJsonConverterFactory Default =
new DictionaryJsonConverterFactoryBuilder().AddDefaults().Build();
private readonly ImmutableDictionary<Type, Delegate> _parsers;
private readonly ImmutableDictionary<Type, Delegate> _serializers;
private readonly Func<Type, JsonConverter> _valueFactory;
private readonly ConcurrentDictionary<Type, JsonConverter> _jsonConverterCache = new ();
internal DictionaryJsonConverterFactory(
ImmutableDictionary<Type, Delegate> parsers,
ImmutableDictionary<Type, Delegate> serializers)
{
_parsers = parsers;
_serializers = serializers;
_valueFactory = CreateConverter;
}
public override bool CanConvert(Type typeToConvert)
{
return
typeToConvert.IsGenericType &&
s_converterTypes.ContainsKey(typeToConvert.GetGenericTypeDefinition()) &&
_parsers.ContainsKey(typeToConvert.GenericTypeArguments[0]);
}
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
return _jsonConverterCache.GetOrAdd(typeToConvert, _valueFactory);
}
private static Converter<T, string> MakeClassSerializer<T>() where T : class
{
return item => item?.ToString() ?? string.Empty;
}
private static Converter<T, string> MakeStructSerializer<T>() where T : struct
{
return item => item.ToString() ?? string.Empty;
}
private JsonConverter CreateConverter(Type typeToConvert)
{
var keyType = typeToConvert.GenericTypeArguments[0];
var valueType = typeToConvert.GenericTypeArguments[1];
var keyParser = _parsers[keyType];
if (!_serializers.TryGetValue(keyType, out var keySerializer))
{
var methodName = keyType.IsValueType ? nameof(MakeStructSerializer) : nameof(MakeClassSerializer);
var obj = GetType()
.GetMethod(methodName, BindingFlags.Static | BindingFlags.NonPublic)?
.MakeGenericMethod(keyType)
.Invoke(null, null) ?? throw new NullReferenceException("Failed to invoke serializer maker");
keySerializer = (Delegate)obj;
}
var converterType = s_converterTypes[typeToConvert.GetGenericTypeDefinition()];
var result = Activator.CreateInstance(
converterType.MakeGenericType(typeToConvert.GenericTypeArguments),
keyParser,
keySerializer) ?? throw new NullReferenceException("Failed to create converter");
return (JsonConverter)result;
}
}
public class DictionaryJsonConverter<TKey, TValue> : JsonConverter<Dictionary<TKey, TValue>?> where TKey : notnull
{
public static Dictionary<TKey, TValue>? Read(
ref Utf8JsonReader reader,
Converter<string?, TKey> keyParser,
JsonSerializerOptions options
)
{
if (reader.TokenType == JsonTokenType.Null)
{
return null;
}
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException("Dictionary must be JSON object.");
}
var result = new Dictionary<TKey, TValue>();
while (true)
{
if (!reader.Read())
{
throw new JsonException("Incomplete JSON object");
}
if (reader.TokenType == JsonTokenType.EndObject)
{
return result;
}
var key = keyParser(reader.GetString());
if (!reader.Read())
{
throw new JsonException("Incomplete JSON object");
}
var value = JsonSerializer.Deserialize<TValue>(ref reader, options);
result.Add(key, value!);
}
}
private readonly Converter<string?, TKey> _keyParser;
private readonly Converter<TKey, string> _keySerializer;
public DictionaryJsonConverter(
Converter<string?, TKey> keyParser,
Converter<TKey, string> keySerializer
)
{
_keyParser = keyParser;
_keySerializer = keySerializer;
}
public override Dictionary<TKey, TValue>? Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options
)
{
return Read(ref reader, _keyParser, options);
}
public override void Write(
Utf8JsonWriter writer,
Dictionary<TKey, TValue>? value,
JsonSerializerOptions options
)
{
if (value is null)
{
writer.WriteNullValue();
}
else
{
writer.WriteStartObject();
foreach (var pair in value)
{
writer.WritePropertyName(_keySerializer(pair.Key));
JsonSerializer.Serialize(writer, pair.Value, options);
}
writer.WriteEndObject();
}
}
}