Skip to content

Instantly share code, notes, and snippets.

@lukencode
Created July 22, 2010 03:52
Show Gist options
  • Save lukencode/485545 to your computer and use it in GitHub Desktop.
Save lukencode/485545 to your computer and use it in GitHub Desktop.
Alternate RestSharp Json Deserializer
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json.Linq;
using RestSharp;
using RestSharp.Deserializers;
using RestSharp.Extensions;
namespace Shared.Utilities
{
public class ExtendedJsonDeserializer : IDeserializer
{
public string RootElement { get; set; }
public string Namespace { get; set; }
public string DateFormat { get; set; }
public T Deserialize<T>(RestResponse response) where T : new()
{
var target = new T();
if (target is IList)
{
var objType = target.GetType();
JArray json = JArray.Parse(response.Content);
target = (T)BuildList(objType, json.Root.Children());
}
else
{
JObject json = JObject.Parse(response.Content);
JToken root = json.Root;
if (RootElement.HasValue())
root = json[RootElement];
Map(target, root);
}
return target;
}
private void Map(object x, JToken json)
{
var objType = x.GetType();
var props = objType.GetProperties().Where(p => p.CanWrite).ToList();
foreach (var prop in props)
{
var type = prop.PropertyType;
var name = prop.Name;
var value = json[name];
var actualName = name;
if (value == null)
{
// try camel cased name
actualName = name.ToCamelCase();
value = json[actualName];
}
if (value == null)
{
// try lower cased name
actualName = name.ToLower();
value = json[actualName];
}
if (value == null)
{
// try name with underscores
actualName = name.AddUnderscores();
value = json[actualName];
}
if (value == null)
{
// try name with underscores with lower case
actualName = name.AddUnderscores().ToLower();
value = json[actualName];
}
if (value == null || value.Type == JTokenType.Null)
{
continue;
}
// check for nullable and extract underlying type
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
{
type = type.GetGenericArguments()[0];
}
prop.SetValue(x, CreateType(value, type), null);
}
}
private object CreateAndMap(Type type, JToken element)
{
object instance = null;
if (type.IsGenericType)
{
var genericTypeDef = type.GetGenericTypeDefinition();
if (genericTypeDef == typeof(Dictionary<,>))
{
instance = BuildDictionary(type, element.Children());
}
else if (genericTypeDef == typeof(List<>))
{
instance = BuildList(type, element.Children());
}
}
else
{
instance = Activator.CreateInstance(type);
Map(instance, element);
}
return instance;
}
private IDictionary BuildDictionary(Type type, JEnumerable<JToken> elements)
{
var dict = (IDictionary)Activator.CreateInstance(type);
var valueType = type.GetGenericArguments()[1];
foreach (JProperty child in elements)
{
var key = child.Name;
var item = CreateAndMap(valueType, child.Value);
dict.Add(key, item);
}
return dict;
}
private IList BuildList(Type type, JEnumerable<JToken> elements)
{
var list = (IList)Activator.CreateInstance(type);
var itemType = type.GetGenericArguments()[0];
foreach (var element in elements)
{
list.Add(CreateType(element, itemType));
}
return list;
}
private object CreateType(JToken obj, Type type)
{
object returnObj = null;
if (type.IsPrimitive)
{
// no primitives can contain quotes so we can safely remove them
// allows converting a json value like {"index": "1"} to an int
var tmpVal = obj.ToString().Replace("\"", string.Empty);
returnObj = tmpVal;
}
else if (type == typeof(string))
{
string raw = obj.ToString();
// remove leading and trailing "
string clean = raw.Substring(1, raw.Length - 2);
returnObj = clean;
}
else if (type == typeof(DateTime))
{
DateTime dt;
if (DateFormat.HasValue())
{
var clean = obj.ToString().RemoveSurroundingQuotes();
dt = DateTime.ParseExact(clean, DateFormat, CultureInfo.InvariantCulture);
}
else
{
// try parsing instead
dt = obj.ToString().ParseJsonDate();
}
returnObj = dt;
}
else if (type == typeof(Decimal))
{
var dec = Decimal.Parse(obj.ToString());
returnObj = dec;
}
else if (type == typeof(Guid))
{
string raw = obj.ToString();
var guid = new Guid(raw.Substring(1, raw.Length - 2));
returnObj = guid;
}
else if (type.IsGenericType)
{
var genericTypeDef = type.GetGenericTypeDefinition();
if (genericTypeDef == typeof(List<>))
{
var list = BuildList(type, obj.Children());
returnObj = list;
}
else if (genericTypeDef == typeof(Dictionary<,>))
{
var keyType = type.GetGenericArguments()[0];
// only supports Dict<string, T>()
if (keyType == typeof(string))
{
var dict = BuildDictionary(type, obj.Children());
returnObj = dict;
}
}
}
else
{
var item = CreateAndMap(type, obj);
returnObj = item;
}
return returnObj;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment