Skip to content

Instantly share code, notes, and snippets.

@Turnerj
Created December 1, 2019 04:53
Show Gist options
  • Save Turnerj/11bb3832e4d4a40464768311de3d46e8 to your computer and use it in GitHub Desktop.
Save Turnerj/11bb3832e4d4a40464768311de3d46e8 to your computer and use it in GitHub Desktop.
Custom "Thing" converter for Schema.NET
namespace Schema.NET
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
/// <summary>
/// Converts IThing
/// </summary>
public class ThingJsonConverter : JsonConverter<IThing>
{
private static ConcurrentDictionary<Type, PropertyInfo[]> ThingProperties { get; } = new ConcurrentDictionary<Type, PropertyInfo[]>();
private static ConcurrentDictionary<PropertyInfo, string> PropertyNameMapping { get; } = new ConcurrentDictionary<PropertyInfo, string>();
private static ConcurrentDictionary<Type, JsonConverter> ConverterLookup { get; } = new ConcurrentDictionary<Type, JsonConverter>();
/// <inheritdoc/>
public override bool CanConvert(Type typeToConvert) => typeof(IThing).IsAssignableFrom(typeToConvert);
/// <inheritdoc/>
public override IThing Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (typeToConvert == null || options == null)
{
throw new ArgumentNullException(nameof(typeToConvert));
}
var result = Activator.CreateInstance(typeToConvert) as IThing;
var document = JsonDocument.ParseValue(ref reader).RootElement;
foreach (var property in this.GetProperties(typeToConvert))
{
if (property.CanWrite && PropertyNameMapping.TryGetValue(property, out var propertyName) && document.TryGetProperty(propertyName, out var value))
{
var realValue = value.ToObject(property.PropertyType);
property.SetValue(result, realValue);
}
}
return result;
}
/// <inheritdoc/>
public override void Write(Utf8JsonWriter writer, IThing value, JsonSerializerOptions options)
{
if (writer == null)
{
throw new ArgumentNullException(nameof(writer));
}
if (value == null)
{
writer.WriteNullValue();
}
else
{
writer.WriteStartObject();
var type = value?.GetType();
foreach (var property in this.GetProperties(type))
{
this.WriteProperty(writer, value, property, options);
}
writer.WriteEndObject();
}
}
private void WriteProperty(Utf8JsonWriter writer, IThing value, PropertyInfo property, JsonSerializerOptions options)
{
var propertyValue = property.GetValue(value);
var propertyType = property.PropertyType;
if (!PropertyNameMapping.TryGetValue(property, out var propertyName))
{
var jsonPropertyNameAttr = property.GetCustomAttribute<JsonPropertyNameAttribute>();
propertyName = jsonPropertyNameAttr.Name;
PropertyNameMapping.TryAdd(property, propertyName);
}
if (propertyValue is IValues valuesCollection && valuesCollection.Count == 0)
{
return;
}
writer.WritePropertyName(propertyName);
var converterAttr = property.GetCustomAttribute<JsonConverterAttribute>();
if (converterAttr != null)
{
var converterType = converterAttr.ConverterType;
if (!ConverterLookup.TryGetValue(converterType, out var converter))
{
converter = Activator.CreateInstance(converterType) as JsonConverter;
ConverterLookup.TryAdd(converterType, converter);
}
var writeMethod = converterType.GetMethod("Write", BindingFlags.Public | BindingFlags.Instance);
writeMethod.Invoke(converter, new[] { writer, propertyValue, options });
}
else
{
JsonSerializer.Serialize(writer, propertyValue, propertyType, options);
}
}
private PropertyInfo[] GetProperties(Type type)
{
if (!ThingProperties.TryGetValue(type, out var properties))
{
properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
ThingProperties.TryAdd(type, properties);
}
return properties;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment