JsonInheritanceConverter<TBase>
Defines the class as inheritance base class and adds a discriminator property to the serialized object.
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NJsonSchema.Converters
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(new byte[] {
0,
2
})]
public class JsonInheritanceConverter<[System.Runtime.CompilerServices.Nullable(2)] TBase> : JsonConverter<TBase>
{
private readonly string _discriminatorName;
public static IDictionary<string, Type> AdditionalKnownTypes { get; } = new Dictionary<string, Type>();
public virtual string DiscriminatorName => _discriminatorName;
public JsonInheritanceConverter()
{
_discriminatorName = (CustomAttributeExtensions.GetCustomAttribute<JsonInheritanceConverterAttribute>((MemberInfo)typeof(TBase))?.DiscriminatorName ?? "discriminator");
}
public JsonInheritanceConverter(string discriminatorName)
{
_discriminatorName = discriminatorName;
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public override TBase Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
JsonDocument jsonDocument = JsonDocument.ParseValue(ref reader);
JsonElement rootElement = jsonDocument.RootElement;
JsonElement value;
bool flag = rootElement.TryGetProperty(_discriminatorName, out value);
Type discriminatorType = GetDiscriminatorType(jsonDocument.RootElement, typeToConvert, flag ? value.GetString() : null);
MemoryStream memoryStream = new MemoryStream();
using (Utf8JsonWriter writer = new Utf8JsonWriter(memoryStream, default(JsonWriterOptions))) {
rootElement = jsonDocument.RootElement;
rootElement.WriteTo(writer);
}
return (TBase)JsonSerializer.Deserialize(memoryStream.ToArray(), discriminatorType, options);
}
public override void Write(Utf8JsonWriter writer, [System.Runtime.CompilerServices.Nullable(2)] TBase value, JsonSerializerOptions options)
{
if (value != null) {
writer.WriteStartObject();
writer.WriteString(_discriminatorName, GetDiscriminatorValue(value.GetType()));
byte[] array = JsonSerializer.SerializeToUtf8Bytes<object>((object)value, options);
JsonDocument jsonDocument = JsonDocument.Parse(array, default(JsonDocumentOptions));
foreach (JsonProperty item in jsonDocument.RootElement.EnumerateObject()) {
item.WriteTo(writer);
}
writer.WriteEndObject();
} else
writer.WriteNullValue();
}
public virtual string GetDiscriminatorValue(Type type)
{
KeyValuePair<string, Type> keyValuePair = Enumerable.SingleOrDefault<KeyValuePair<string, Type>>((IEnumerable<KeyValuePair<string, Type>>)AdditionalKnownTypes, (Func<KeyValuePair<string, Type>, bool>)((KeyValuePair<string, Type> p) => p.Value == type));
if (keyValuePair.Key != null)
return keyValuePair.Key;
string subtypeDiscriminator = GetSubtypeDiscriminator(type);
if (subtypeDiscriminator != null)
return subtypeDiscriminator;
return type.Name;
}
protected virtual Type GetDiscriminatorType(JsonElement jObject, Type objectType, [System.Runtime.CompilerServices.Nullable(2)] string discriminatorValue)
{
if (discriminatorValue != null) {
if (AdditionalKnownTypes.ContainsKey(discriminatorValue))
return AdditionalKnownTypes[discriminatorValue];
Type objectSubtype = GetObjectSubtype(objectType, discriminatorValue);
if (objectSubtype != (Type)null)
return objectSubtype;
if (objectType.Name == discriminatorValue)
return objectType;
Type subtypeFromKnownTypeAttributes = GetSubtypeFromKnownTypeAttributes(objectType, discriminatorValue);
if (subtypeFromKnownTypeAttributes != (Type)null)
return subtypeFromKnownTypeAttributes;
string name = objectType.Namespace + "." + discriminatorValue;
Type type = objectType.GetTypeInfo().Assembly.GetType(name);
if (type != (Type)null)
return type;
}
throw new InvalidOperationException("Could not find subtype of '" + objectType.Name + "' with discriminator '" + discriminatorValue + "'.");
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private static Type GetSubtypeFromKnownTypeAttributes(Type objectType, string discriminatorValue)
{
Type type = objectType;
do {
IEnumerable<object> enumerable = Enumerable.Where<object>((IEnumerable<object>)type.GetTypeInfo().GetCustomAttributes(false), (Func<object, bool>)((object a) => a.GetType().Name == "KnownTypeAttribute"));
foreach (dynamic item in enumerable) {
dynamic val = item.Type != null;
if ((val ? false : true) ? val : (val & (item.Type.Name == discriminatorValue)))
return (Type)item.Type;
if (item.MethodName != null) {
MethodInfo runtimeMethod = type.GetRuntimeMethod((string)item.MethodName, new Type[0]);
if (runtimeMethod != (MethodInfo)null) {
IEnumerable<Type> enumerable2 = runtimeMethod.Invoke(null, new object[0]) as IEnumerable<Type>;
if (enumerable2 != null) {
foreach (Type item2 in enumerable2) {
if (item2.Name == discriminatorValue)
return item2;
}
}
return null;
}
}
}
type = type.GetTypeInfo().BaseType;
} while (type != (Type)null);
return null;
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private static Type GetObjectSubtype(Type baseType, string discriminatorValue)
{
IEnumerable<JsonInheritanceAttribute> source = Enumerable.OfType<JsonInheritanceAttribute>((IEnumerable)baseType.GetTypeInfo().GetCustomAttributes(true));
return Enumerable.SingleOrDefault<JsonInheritanceAttribute>(source, (Func<JsonInheritanceAttribute, bool>)((JsonInheritanceAttribute a) => a.Key == discriminatorValue))?.Type;
}
[return: System.Runtime.CompilerServices.Nullable(2)]
private static string GetSubtypeDiscriminator(Type objectType)
{
IEnumerable<JsonInheritanceAttribute> source = Enumerable.OfType<JsonInheritanceAttribute>((IEnumerable)objectType.GetTypeInfo().GetCustomAttributes(true));
return Enumerable.SingleOrDefault<JsonInheritanceAttribute>(source, (Func<JsonInheritanceAttribute, bool>)((JsonInheritanceAttribute a) => a.Type == objectType))?.Key;
}
}
}