Created
January 14, 2017 08:09
-
-
Save Yortw/e568b363405577d55d1b68cad8279851 to your computer and use it in GitHub Desktop.
UWP Json.Net CachedReflection Converter
This file contains hidden or 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 sealed class CachedReflectionJsonConverter : JsonConverter | |
{ | |
private IEnumerable<Type> _HandledTypes; | |
private static Dictionary<Type, CachedTypeInformation> _ReflectionCache = new Dictionary<Type, CachedTypeInformation>(); | |
public CachedReflectionJsonConverter(IEnumerable<Type> handledTypes) | |
{ | |
_HandledTypes = handledTypes.ToArray(); | |
} | |
public override bool CanConvert(Type objectType) | |
{ | |
return _HandledTypes.Contains(objectType); | |
} | |
public override bool CanRead | |
{ | |
get | |
{ | |
return true; | |
} | |
} | |
public override bool CanWrite | |
{ | |
get | |
{ | |
return false; | |
} | |
} | |
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) | |
{ | |
CachedTypeInformation cachedInfo = null; | |
if (!_ReflectionCache.TryGetValue(objectType, out cachedInfo)) | |
{ | |
cachedInfo = new CachedTypeInformation(); | |
cachedInfo.WriteableProperties = | |
( | |
from p | |
in objectType.GetProperties(BindingFlags.Public | BindingFlags.Instance) | |
where p.CanWrite | |
select p | |
).ToDictionary((p) => p.Name, StringComparer.OrdinalIgnoreCase); | |
cachedInfo.Constructor = objectType.GetConstructor(Type.EmptyTypes); | |
_ReflectionCache[objectType] = cachedInfo; | |
} | |
var retVal = cachedInfo.Constructor.Invoke(null); | |
PropertyInfo propertyInfo = null; | |
var moreToRead = reader.Read(); | |
while (moreToRead && reader.TokenType != JsonToken.EndObject) | |
{ | |
if (reader.TokenType == JsonToken.PropertyName) | |
{ | |
var name = (string)reader.Value; | |
cachedInfo.WriteableProperties.TryGetValue(name, out propertyInfo); | |
moreToRead = reader.Read(); | |
if (propertyInfo != null) | |
{ | |
switch (reader.TokenType) | |
{ | |
case JsonToken.String: | |
propertyInfo.SetValue(retVal, reader.Value); | |
break; | |
case JsonToken.Float: | |
case JsonToken.Integer: | |
case JsonToken.Boolean: | |
case JsonToken.Date: | |
if (propertyInfo.PropertyType == typeof(decimal)) | |
propertyInfo.SetValue(retVal, System.Convert.ChangeType(reader.Value, propertyInfo.PropertyType)); | |
else if (reader.TokenType == JsonToken.Integer && propertyInfo.PropertyType != typeof(Int64)) | |
propertyInfo.SetValue(retVal, System.Convert.ChangeType(reader.Value, propertyInfo.PropertyType)); | |
else | |
propertyInfo.SetValue(retVal, reader.Value); | |
break; | |
case JsonToken.Null: | |
propertyInfo.SetValue(retVal, reader.Value); | |
break; | |
case JsonToken.StartObject: | |
case JsonToken.StartArray: | |
propertyInfo.SetValue(retVal, serializer.Deserialize(reader, propertyInfo.PropertyType)); | |
break; | |
default: | |
throw new InvalidOperationException(); | |
} | |
} | |
} | |
moreToRead = reader.Read(); | |
} | |
return retVal; | |
} | |
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) | |
{ | |
throw new NotImplementedException(); | |
} | |
private sealed class CachedTypeInformation | |
{ | |
public ConstructorInfo Constructor { get; set; } | |
public Dictionary<string, PropertyInfo> WriteableProperties { get; set; } | |
} | |
} |
This can be further improved by dumping the 'handledTypes' stuff and either applying the converter as an attribute to your entity classes or if that's not posible implementing a custom contract coverter (http://www.newtonsoft.com/json/help/html/Performance.htm).
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Completely unscientific testing shows this to be 25-50% faster on release builds of UWP apps. YMMV. Not production code, test for your use cases and modify appropriately. Only handles simple cases, e.g. doesn't obey Json.Net attributes for mapping property names etc.
Only handles singular objects (not arrays/collections - those are left for Json.Net to handle using it's default logic). No doubt many improvements that could be made.
Sample usage:
`
`