Created
March 9, 2019 02:31
-
-
Save ejlevin1/584a06e0b00b97fec22fef0f271a3669 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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