Skip to content

Instantly share code, notes, and snippets.

@jeffijoe
Last active October 2, 2018 20:31
Show Gist options
  • Save jeffijoe/ac113a96dfae75e6f85a78bd5b394475 to your computer and use it in GitHub Desktop.
Save jeffijoe/ac113a96dfae75e6f85a78bd5b394475 to your computer and use it in GitHub Desktop.
JsonConverter based on type
// SkyClip
// - GenericTypeResolver.cs
// --------------------------------------------------------------------
// Author: Jeff Hansen <jeff@jeffijoe.com>
// Copyright (C) Jeff Hansen 2015. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace SkyClip.Common.Misc
{
/// <summary>
/// Class that can resolve a type based on input of another type.
/// </summary>
/// <typeparam name="TKey">
/// The type of the key.
/// </typeparam>
public class GenericTypeResolver<TKey>
{
#region Fields
/// <summary>
/// The assemblies.
/// </summary>
private readonly Assembly[] assemblies;
/// <summary>
/// The cache.
/// </summary>
private readonly Dictionary<TKey, Type> cache;
/// <summary>
/// The format string.
/// </summary>
private readonly string formatString;
/// <summary>
/// The locker.
/// </summary>
private readonly object locker = new object();
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="GenericTypeResolver{TKey}"/> class.
/// </summary>
/// <param name="formatString">
/// The format string used when concatting the given key to resolve the type.
/// </param>
/// <param name="typesInAssembliesToBeSearched">
/// Types in assemblies to be searched. If null or empty, the assembly
/// containing the type of the <see cref="TKey"/> is used.
/// </param>
public GenericTypeResolver(string formatString = "{0}", params Type[] typesInAssembliesToBeSearched)
{
this.formatString = formatString;
if (typesInAssembliesToBeSearched == null || typesInAssembliesToBeSearched.Length == 0)
{
this.assemblies = new[]
{
typeof(TKey).GetTypeInfo().Assembly
};
}
else
{
this.assemblies = typesInAssembliesToBeSearched.Select(x => x.GetTypeInfo().Assembly).ToArray();
}
this.cache = new Dictionary<TKey, Type>();
}
#endregion
#region Public Methods and Operators
/// <summary>
/// Resolves the specified key.
/// </summary>
/// <param name="key">
/// The key.
/// </param>
/// <returns>
/// The resolved type, or <c>null</c> if not found.
/// </returns>
public Type Resolve(TKey key)
{
Type type;
if (this.cache.TryGetValue(key, out type))
{
return type;
}
lock (this.locker)
{
if (this.cache.TryGetValue(key, out type))
{
return type;
}
var str = string.Format(this.formatString, key);
var firstOrDefault = (from asm in this.assemblies
from t in asm.DefinedTypes
where t.Name == str
select t).FirstOrDefault();
if (firstOrDefault == null)
{
return null;
}
type = firstOrDefault.AsType();
this.cache.Add(key, type);
return type;
}
}
#endregion
}
}
// SkyClip
// - TypeResolutionBasedConverter.cs
// --------------------------------------------------------------------
// Author: Jeff Hansen <jeff@jeffijoe.com>
// Copyright (C) Jeff Hansen 2015. All rights reserved.
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SkyClip.Common.Misc;
namespace SkyClip.Common.Converters.Base
{
/// <summary>
/// Type resolution based converter. Uses a <see cref="GenericTypeResolver{TKey}"/> to resolve the type.
/// </summary>
/// <typeparam name="TTypeKey">
/// The type of the type key.
/// </typeparam>
public abstract class TypeResolutionBasedConverter<TTypeKey> : JsonConverter
{
#region Fields
/// <summary>
/// The resolver.
/// </summary>
private readonly GenericTypeResolver<TTypeKey> resolver;
#endregion
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="TypeResolutionBasedConverter{TTypeKey}"/> class.
/// </summary>
/// <param name="formatString">
/// The format string.
/// </param>
/// <param name="typesInAssembliesToBeSearched">
/// The types in assemblies to be searched.
/// </param>
protected TypeResolutionBasedConverter(string formatString = "{0}", params Type[] typesInAssembliesToBeSearched)
{
this.resolver = new GenericTypeResolver<TTypeKey>(formatString, typesInAssembliesToBeSearched);
}
#endregion
#region Public Properties
/// <summary>
/// Gets a value indicating whether this <see cref="T:Newtonsoft.Json.JsonConverter" /> can write JSON.
/// </summary>
/// <value>
/// <c>true</c> if this <see cref="T:Newtonsoft.Json.JsonConverter" /> can write JSON; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite
{
get
{
return false;
}
}
#endregion
#region Public Methods and Operators
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">
/// The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.
/// </param>
/// <param name="objectType">
/// Type of the object.
/// </param>
/// <param name="existingValue">
/// The existing value of object being read.
/// </param>
/// <param name="serializer">
/// The calling serializer.
/// </param>
/// <returns>
/// The object value.
/// </returns>
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
var key = this.ParseKey(jsonObject);
var clipClrType = this.resolver.Resolve(key);
var target = Activator.CreateInstance(clipClrType);
serializer.Populate(jsonObject.CreateReader(), target);
return target;
}
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">
/// The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.
/// </param>
/// <param name="value">
/// The value.
/// </param>
/// <param name="serializer">
/// The calling serializer.
/// </param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
}
#endregion
#region Methods
/// <summary>
/// Parses the key used for type resolution from the json object.
/// </summary>
/// <param name="jsonObject">
/// The json object.
/// </param>
/// <returns>
/// The key.
/// </returns>
protected abstract TTypeKey ParseKey(JObject jsonObject);
#endregion
}
}
// SkyClip
// - EventMessageJsonConverter.cs
// --------------------------------------------------------------------
// Author: Jeff Hansen <jeff@jeffijoe.com>
// Copyright (C) Jeff Hansen 2015. All rights reserved.
using System;
using System.Reflection;
using Newtonsoft.Json.Linq;
using SkyClip.Common.Converters.Base;
using SkyClip.Common.Dtos.EventMessages;
namespace SkyClip.Common.Converters
{
/// <summary>
/// Event Message JSON Converter.
/// </summary>
public class EventMessageJsonConverter : TypeResolutionBasedConverter<string>
{
#region Constructors and Destructors
/// <summary>
/// Initializes a new instance of the <see cref="EventMessageJsonConverter" /> class.
/// </summary>
public EventMessageJsonConverter()
: base("{0}", typeof(EventMessage))
{
}
#endregion
#region Public Methods and Operators
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">
/// Type of the object.
/// </param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return typeof(EventMessage).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
#endregion
#region Methods
/// <summary>
/// Parses the key used for type resolution from the json object.
/// </summary>
/// <param name="jsonObject">
/// The json object.
/// </param>
/// <returns>
/// The key.
/// </returns>
protected override string ParseKey(JObject jsonObject)
{
return jsonObject.Value<string>("messageType");
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment