Skip to content

Instantly share code, notes, and snippets.

@Yortw
Created January 14, 2017 08:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Yortw/e568b363405577d55d1b68cad8279851 to your computer and use it in GitHub Desktop.
Save Yortw/e568b363405577d55d1b68cad8279851 to your computer and use it in GitHub Desktop.
UWP Json.Net CachedReflection Converter
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; }
}
}
@Yortw
Copy link
Author

Yortw commented Jan 14, 2017

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:
`

		var serialiser = new Newtonsoft.Json.JsonSerializer();

		serialiser.Converters.Add
		(
		// Will deserialise the Product and Sku classes using the custom converter, other types handled by Json.Net
			new CachedReflectionJsonConverter
			(
				new Type[] { typeof(Product), typeof(Sku) }
			)
		);

		using (var reader = await GetJsonReader())
		{
			var results = serialiser.Deserialize<Dictionary<string, Product>>(reader);
		}

`

@Yortw
Copy link
Author

Yortw commented Jan 15, 2017

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