Last active
January 3, 2016 16:00
-
-
Save akrisiun/52a4c9578ab9f2d6ad7d to your computer and use it in GitHub Desktop.
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
// https://github.com/pbhogan/TinyJSON/blob/master/TinyJSON/JSON.cs | |
public static void MakeInto<T>( Variant data, out T item ) | |
{ | |
item = DecodeType<T>( data ); | |
} | |
private static Dictionary<string,Type> typeCache = new Dictionary<string,Type>(); | |
private static Type FindType( string fullName ) | |
{ | |
if (fullName == null) | |
{ | |
return null; | |
} | |
Type type; | |
if (typeCache.TryGetValue( fullName, out type )) | |
{ | |
return type; | |
} | |
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) | |
{ | |
type = assembly.GetType( fullName ); | |
if (type != null) | |
{ | |
typeCache.Add( fullName, type ); | |
return type; | |
} | |
} | |
return null; | |
} | |
private static T DecodeType<T>( Variant data ) | |
{ | |
if (data == null) | |
{ | |
return default(T); | |
} | |
var type = typeof(T); | |
if (type.IsEnum) | |
{ | |
return (T) Enum.Parse( type, data.ToString() ); | |
} | |
if (type.IsPrimitive || type == typeof(string) || type == typeof(decimal)) | |
{ | |
return (T) Convert.ChangeType( data, type ); | |
} | |
if (type.IsArray) | |
{ | |
if (type.GetArrayRank() == 1) | |
{ | |
var makeFunc = decodeArrayMethod.MakeGenericMethod( new Type[] { type.GetElementType() } ); | |
return (T) makeFunc.Invoke( null, new object[] { data } ); | |
} | |
else | |
{ | |
var arrayData = data as ProxyArray; | |
var arrayRank = type.GetArrayRank(); | |
var rankLengths = new int[arrayRank]; | |
if (arrayData.CanBeMultiRankArray( rankLengths )) | |
{ | |
var array = Array.CreateInstance( type.GetElementType(), rankLengths ); | |
var makeFunc = decodeMultiRankArrayMethod.MakeGenericMethod( new Type[] { type.GetElementType() } ); | |
try | |
{ | |
makeFunc.Invoke( null, new object[] { arrayData, array, 1, rankLengths } ); | |
} | |
catch (Exception e) | |
{ | |
throw new DecodeException( "Error decoding multidimensional array. Did you try to decode into an array of incompatible rank or element type?", e ); | |
} | |
return (T) Convert.ChangeType( array, typeof(T) ); | |
} | |
throw new DecodeException( "Error decoding multidimensional array; JSON data doesn't seem fit this structure." ); | |
#pragma warning disable 0162 | |
return default(T); | |
#pragma warning restore 0162 | |
} | |
} | |
if (typeof(IList).IsAssignableFrom( type )) | |
{ | |
var makeFunc = decodeListMethod.MakeGenericMethod( type.GetGenericArguments() ); | |
return (T) makeFunc.Invoke( null, new object[] { data } ); | |
} | |
if (typeof(IDictionary).IsAssignableFrom( type )) | |
{ | |
var makeFunc = decodeDictionaryMethod.MakeGenericMethod( type.GetGenericArguments() ); | |
return (T) makeFunc.Invoke( null, new object[] { data } ); | |
} | |
// At this point we should be dealing with a class or struct. | |
T instance; | |
var proxyObject = data as ProxyObject; | |
if (proxyObject == null) | |
{ | |
throw new InvalidCastException( "ProxyObject expected when decoding into '" + type.FullName + "'." ); | |
} | |
// If there's a type hint, use it to create the instance. | |
var typeHint = proxyObject.TypeHint; | |
if (typeHint != null && typeHint != type.FullName) | |
{ | |
var makeType = FindType( typeHint ); | |
if (makeType == null) | |
{ | |
throw new TypeLoadException( "Could not load type '" + typeHint + "'." ); | |
} | |
else | |
{ | |
if (type.IsAssignableFrom( makeType )) | |
{ | |
instance = (T) Activator.CreateInstance( makeType ); | |
type = makeType; | |
} | |
else | |
{ | |
throw new InvalidCastException( "Cannot assign type '" + typeHint + "' to type '" + type.FullName + "'." ); | |
} | |
} | |
} | |
else | |
{ | |
// We don't have a type hint, so just instantiate the type we have. | |
instance = Activator.CreateInstance<T>(); | |
} | |
// Now decode fields and properties. | |
foreach (var pair in data as ProxyObject) | |
{ | |
var field = type.GetField( pair.Key, instanceBindingFlags ); | |
if (field != null) | |
{ | |
var shouldDecode = field.IsPublic; | |
foreach (var attribute in field.GetCustomAttributes( true )) | |
{ | |
if (excludeAttrType.IsAssignableFrom( attribute.GetType() )) | |
{ | |
shouldDecode = false; | |
} | |
if (includeAttrType.IsAssignableFrom( attribute.GetType() )) | |
{ | |
shouldDecode = true; | |
} | |
} | |
if (shouldDecode) | |
{ | |
var makeFunc = decodeTypeMethod.MakeGenericMethod( new Type[] { field.FieldType } ); | |
if (type.IsValueType) | |
{ | |
// Type is a struct. | |
var instanceRef = (object) instance; | |
field.SetValue( instanceRef, makeFunc.Invoke( null, new object[] { pair.Value } ) ); | |
instance = (T) instanceRef; | |
} | |
else | |
{ | |
// Type is a class. | |
field.SetValue( instance, makeFunc.Invoke( null, new object[] { pair.Value } ) ); | |
} | |
} | |
} | |
var property = type.GetProperty( pair.Key, instanceBindingFlags ); | |
if (property != null) | |
{ | |
if (property.CanWrite && property.GetCustomAttributes( false ).AnyOfType( includeAttrType )) | |
{ | |
var makeFunc = decodeTypeMethod.MakeGenericMethod( new Type[] { property.PropertyType } ); | |
if (type.IsValueType) | |
{ | |
// Type is a struct. | |
var instanceRef = (object) instance; | |
property.SetValue( instanceRef, makeFunc.Invoke( null, new object[] { pair.Value } ), null ); | |
instance = (T) instanceRef; | |
} | |
else | |
{ | |
// Type is a class. | |
property.SetValue( instance, makeFunc.Invoke( null, new object[] { pair.Value } ), null ); | |
} | |
} | |
} | |
} | |
// Invoke methods tagged with [AfterDecode] attribute. | |
foreach (var method in type.GetMethods( instanceBindingFlags )) | |
{ | |
if (method.GetCustomAttributes( false ).AnyOfType( typeof(AfterDecode) )) | |
{ | |
if (method.GetParameters().Length == 0) | |
{ | |
method.Invoke( instance, null ); | |
} | |
else | |
{ | |
method.Invoke( instance, new object[] { data } ); | |
} | |
} | |
} | |
return instance; | |
} | |
private static List<T> DecodeList<T>( Variant data ) | |
{ | |
var list = new List<T>(); | |
foreach (var item in data as ProxyArray) | |
{ | |
list.Add( DecodeType<T>( item ) ); | |
} | |
return list; | |
} | |
private static Dictionary<K,V> DecodeDictionary<K,V>( Variant data ) | |
{ | |
var dict = new Dictionary<K,V>(); | |
var type = typeof(K); | |
foreach (var pair in data as ProxyObject) | |
{ | |
var k = (K) (type.IsEnum ? Enum.Parse( type, pair.Key ) : Convert.ChangeType( pair.Key, type )); | |
var v = DecodeType<V>( pair.Value ); | |
dict.Add( k, v ); | |
} | |
return dict; | |
} | |
private static T[] DecodeArray<T>( Variant data ) | |
{ | |
var arrayData = data as ProxyArray; | |
var arraySize = arrayData.Count; | |
var array = new T[arraySize]; | |
int i = 0; | |
foreach (var item in data as ProxyArray) | |
{ | |
array[i++] = DecodeType<T>( item ); | |
} | |
return array; | |
} | |
private static void DecodeMultiRankArray<T>( ProxyArray arrayData, Array array, int arrayRank, int[] indices ) | |
{ | |
var count = arrayData.Count; | |
for (int i = 0; i < count; i++) | |
{ | |
indices[arrayRank - 1] = i; | |
if (arrayRank < array.Rank) | |
{ | |
DecodeMultiRankArray<T>( arrayData[i] as ProxyArray, array, arrayRank + 1, indices ); | |
} | |
else | |
{ | |
array.SetValue( DecodeType<T>( arrayData[i] ), indices ); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment