Skip to content

Instantly share code, notes, and snippets.

@ejlevin1
Created March 9, 2019 02:31
Show Gist options
  • Save ejlevin1/584a06e0b00b97fec22fef0f271a3669 to your computer and use it in GitHub Desktop.
Save ejlevin1/584a06e0b00b97fec22fef0f271a3669 to your computer and use it in GitHub Desktop.
public static class MessageConverter
{
private static InheritedTypeConverter<IMessage> _default = null;
public static InheritedTypeConverter<IMessage> Default
{
get
{
// TODO would be nice to have an attribute I could plug onto classes and have it "auto" find the classes through an assembly
if (_default == null)
{
_default = new InheritedTypeConverter<IMessage>();
_default.AddSelector<EventsMessageImpl>((jsonObj) => { return jsonObj["Events"] != null; });
_default.AddSelector<PayloadMessage>((jsonObj) => { return jsonObj["Payload"] != null; });
}
return _default;
}
}
public static IMessage DeserializeObject(string json)
{
var serializer = new JsonSerializerSettings();
serializer.Converters.Add(Default);
return JsonConvert.DeserializeObject<IMessage>(json, serializer);
}
}
// https://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet/
public class InheritedTypeConverter<T> : JsonConverter where T : class
{
public class TypeSelector
{
public Func<JObject, bool> Selector { get; set; }
public Type Type { get; set; }
}
private List<TypeSelector> _selectors = new List<TypeSelector>();
public IReadOnlyList<TypeSelector> Selectors { get { return _selectors.AsReadOnly(); } }
public override bool CanWrite => false;
public override bool CanRead => true;
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public void AddSelector<S>(Func<JObject,bool> selector) where S : T
{
_selectors.Add(new TypeSelector() { Selector = selector, Type = typeof(S) });
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new InvalidOperationException("Use default serialization.");
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jsonObject = JObject.Load(reader);
T msg = null;
foreach(var selector in _selectors)
{
// First has priority
if(msg == null && selector.Selector(jsonObject))
{
msg = (T)Activator.CreateInstance(selector.Type);
}
}
if (msg == null)
throw new InvalidProgramException("Unable to deserialize string with given type selectors.");
serializer.Populate(jsonObject.CreateReader(), msg);
return msg;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment