Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions UnitTests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1166,6 +1166,39 @@ public static void CustomTypes()
Assert.AreEqual(ip.ip, o.ip);
}

public class CollectionDto
{
public IDictionary<string,int> Dictionary = new Dictionary<string, int>();
public IDictionary<int, int> Dictionary2 = new Dictionary<int, int>();
public ISet<string> Set;
public ICollection Collection;
public IList<DateTime> OrderedCollection;
}

[Test]
public static void CollectionInterfaces()
{
var dto = new CollectionDto
{
Dictionary = {{"test", 2}, {"test2", 3}},
Dictionary2 = {{1, 2}, {2, 4}},
Set = new SortedSet<string> {"5", "3", "7"},
Collection = new[] {"2", "8", "5"},
OrderedCollection = new[] {DateTime.Now, DateTime.MaxValue}
};

var s = BJSON.ToBJSON(dto);
var o = BJSON.ToObject<CollectionDto>(s);

Assert.IsNotNull(o);
CollectionAssert.AreEquivalent(dto.Dictionary, o.Dictionary);
CollectionAssert.AreEquivalent(dto.Dictionary2, o.Dictionary2);
CollectionAssert.AreEquivalent(dto.Set, o.Set);
Assert.IsTrue(o.Set.SetEquals(dto.Set));
CollectionAssert.AreEqual(dto.Collection, o.Collection);
CollectionAssert.AreEqual(dto.OrderedCollection, o.OrderedCollection);
}

public class readonlyProps
{
public List<string> Collection { get; }
Expand Down
66 changes: 59 additions & 7 deletions fastBinaryJSON/BJSON.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#endif
using System.IO;
using System.Collections.Specialized;
using System.Linq.Expressions;
using fastJSON;

namespace fastBinaryJSON
Expand Down Expand Up @@ -215,7 +216,7 @@ public static byte[] ToBJSON(object obj, BJSONParameters param)
return new BJSONSerializer(param).ConvertToBJSON(obj);
}
/// <summary>
/// Fill a given object with the binary json represenation
/// Fill a given object with the binary json representation
/// </summary>
/// <param name="input"></param>
/// <param name="json"></param>
Expand Down Expand Up @@ -605,8 +606,8 @@ private object ParseDictionary(Dictionary<string, object> d, Dictionary<string,
break;
default:
{
if (pi.IsGenericType && pi.IsValueType == false)
oset = CreateGenericList((List<object>)v, pi.pt, pi.bt, globaltypes);
if (pi.IsGenericType && !pi.IsValueType)
oset = CreateGenericCollection((List<object>)v, pi.pt, pi.bt, globaltypes);
else if ((pi.IsClass || pi.IsStruct || pi.IsInterface) && v is Dictionary<string, object>)
{
var oo = (Dictionary<string, object>)v;
Expand Down Expand Up @@ -710,11 +711,11 @@ private object CreateArray(List<object> data, Type pt, Type bt, Dictionary<strin
return col;
}

private object CreateGenericList(List<object> data, Type pt, Type bt, Dictionary<string, object> globalTypes)
private object CreateGenericList(IEnumerable<object> data, Type pt, Type bt, Dictionary<string, object> globalTypes)
{
if (pt != typeof(object))
{
IList col = (IList)Reflection.Instance.FastCreateList(pt, data.Count);
var col = (IList)Reflection.Instance.FastCreateList(pt.IsInterface ? Reflection.Instance.GetGenericType(typeof(List<>),bt ?? typeof(object)) : pt, data.Count);
// create an array of objects
foreach (object ob in data)
{
Expand All @@ -738,14 +739,64 @@ private object CreateGenericList(List<object> data, Type pt, Type bt, Dictionary
return data;
}

private object CreateGenericCollection(IEnumerable<object> data, Type pt, Type bt, Dictionary<string, object> globalTypes)
{
if (pt != typeof(object))
{
var objType = pt;
if (pt.IsInterface)
{
#if NET4
// ISet<T> is also a collection interface, but is not implemented by any List or Dictionary, so use HashSet<T> as default implementation
objType = new[] {typeof(List<>), typeof(HashSet<>)}
.Select(t => Reflection.Instance.GetGenericType(t, bt ?? typeof(object)))
.FirstOrDefault(pt.IsAssignableFrom);
#else
objType = Reflection.Instance.GetGenericType(typeof(List<>), bt ?? typeof(object));
if (!pt.IsAssignableFrom(objType))
objType = null;
#endif
if (objType == null)
throw new NotSupportedException("Unable to resolve an implementation type for " + pt.FullName);
}
if (typeof(IList).IsAssignableFrom(objType))
return CreateGenericList(data, objType, bt, globalTypes);
// create an array of objects
var add = Reflection.Instance.GetCollectionAdder(objType);
if (add == null) throw new NotSupportedException("Unable to find an Add() method for collection " + objType.FullName);
var col = Reflection.Instance.FastCreateInstance(objType);

foreach (object ob in data)
{
if (ob is IDictionary)
add(col, ParseDictionary((Dictionary<string, object>) ob, globalTypes, bt, null));

else if (ob is List<object>)
{
if (bt.IsGenericType)
add(col, (List<object>) ob);
else
add(col, ((List<object>) ob).ToArray());
}
else if (ob is typedarray)
add(col, ((typedarray) ob).data.ToArray());
else
add(col, ob);
}
return col;
}
return data;
}

private object CreateStringKeyDictionary(Dictionary<string, object> reader, Type pt, Type[] types, Dictionary<string, object> globalTypes)
{
var col = (IDictionary)Reflection.Instance.FastCreateInstance(pt);
Type arraytype = null;
Type t2 = null;
if (types != null)
t2 = types[1];

var col = (IDictionary)Reflection.Instance.FastCreateInstance(pt.IsInterface ? Reflection.Instance.GetGenericType(typeof(Dictionary<,>), typeof(string), t2??typeof(object)) : pt);

Type generictype = null;
var ga = Reflection.Instance.GetGenericArguments(t2);
if (ga.Length > 0)
Expand Down Expand Up @@ -781,7 +832,6 @@ private object CreateStringKeyDictionary(Dictionary<string, object> reader, Type

private object CreateDictionary(List<object> reader, Type pt, Type[] types, Dictionary<string, object> globalTypes)
{
IDictionary col = (IDictionary)Reflection.Instance.FastCreateInstance(pt);
Type t1 = null;
Type t2 = null;
if (types != null)
Expand All @@ -790,6 +840,8 @@ private object CreateDictionary(List<object> reader, Type pt, Type[] types, Dict
t2 = types[1];
}

var col = (IDictionary)Reflection.Instance.FastCreateInstance(pt.IsInterface && t1!=null ? Reflection.Instance.GetGenericType(typeof(Dictionary<,>), t1, t2??typeof(object)) : pt);

foreach (Dictionary<string, object> values in reader)
{
object key = values["k"];
Expand Down
110 changes: 105 additions & 5 deletions fastBinaryJSON/Reflection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using System.Data;
#endif
using System.Collections.Specialized;
using System.Linq.Expressions;

namespace fastJSON
{
Expand Down Expand Up @@ -94,16 +95,19 @@ private Reflection()
public delegate object GenericGetter(object obj);
private delegate object CreateObject();
private delegate object CreateList(int capacity);
internal delegate void AddItemToCollection(object collection, object item);

private SafeDictionary<Type, string> _tyname = new SafeDictionary<Type, string>(10);
private SafeDictionary<string, Type> _typecache = new SafeDictionary<string, Type>(10);
private SafeDictionary<Type, CreateObject> _constrcache = new SafeDictionary<Type, CreateObject>(10);
private SafeDictionary<Type, CreateList> _conlistcache = new SafeDictionary<Type, CreateList>(10);
private SafeDictionary<Type, Getters[]> _getterscache = new SafeDictionary<Type, Getters[]>(10);
private SafeDictionary<string, Dictionary<string, myPropInfo>> _propertycache = new SafeDictionary<string, Dictionary<string, myPropInfo>>(10);
private SafeDictionary<Type, Type[]> _genericTypes = new SafeDictionary<Type, Type[]>(10);
private SafeDictionary<Type, Type[]> _genericArguments = new SafeDictionary<Type, Type[]>(10);
private SafeDictionary<Type, Type> _genericTypeDef = new SafeDictionary<Type, Type>(10);
private static SafeDictionary<short, OpCode> _opCodes;
private SafeDictionary<GenericTypeKey, Type> _genericTypes = new SafeDictionary<GenericTypeKey, Type>(10);
private SafeDictionary<Type, AddItemToCollection> _genericCollectionAdders = new SafeDictionary<Type, AddItemToCollection>(10);
private static Dictionary<short, OpCode> _opCodes;
private static List<string> _blacklistTypes = new List<string>()
{
"system.configuration.install.assemblyinstaller",
Expand All @@ -114,6 +118,66 @@ private Reflection()
"microsoft.exchange.management.systemmanager.winforms.exchangesettingsprovider"
};

#region private implementation types
/// <summary>
/// Composite key to lookup cached constructed generic types.
/// </summary>
private struct GenericTypeKey : IEquatable<GenericTypeKey>
{
private readonly Type _genericDefinition;
private readonly Type[] _parameters;

public Type[] GetParameters()
{
// Results of this method are not exposed, so we can omit making a copy to keep the instance immutable
return _parameters ?? Type.EmptyTypes;
}

public GenericTypeKey(Type genericDefinition, params Type[] parameters)
{
_genericDefinition = genericDefinition;
// input comes from another class, just make a copy to be sure the array's elements do not change after creation.
_parameters = new Type[parameters.Length];
Array.Copy(parameters, _parameters, parameters.Length);
}

public bool Equals(GenericTypeKey other)
{
if (_genericDefinition != other._genericDefinition || (_parameters?.Length ?? -1) != (other._parameters?.Length ?? -1))
return false;
if (_parameters == null && other._parameters == null) return true; // if one is null the other is too at this point, so checking only one would be enough
for (int i = 0; i < _parameters.Length; i++)
{
if (_parameters[i] != other._parameters[i])
return false;
}
return true;
}

public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
return obj is GenericTypeKey && Equals((GenericTypeKey) obj);
}

public override int GetHashCode()
{
unchecked
{
var hashCode = (_genericDefinition != null ? _genericDefinition.GetHashCode() : 0);
if (_parameters == null) return (hashCode * 397);
hashCode = (hashCode * 397) ^ _parameters.Length.GetHashCode();
foreach (Type t in _parameters)
{
hashCode = (hashCode * 397) ^ (t != null ? t.GetHashCode() : 0);
}
return hashCode;
}
}

}
#endregion

private static bool TryGetOpCode(short code, out OpCode opCode)
{
if (_opCodes != null)
Expand Down Expand Up @@ -205,6 +269,41 @@ internal bool IsTypeRegistered(Type t)
}
#endregion

public AddItemToCollection GetCollectionAdder(Type collectionType)
{
var collType = typeof(IList).IsAssignableFrom(collectionType) ? typeof(IList) : collectionType; // Delegate the call to IList.Add(object) for all types that implement it.
if (_genericCollectionAdders.TryGetValue(collType, out var adder))
return adder;

var itemType = collType.IsGenericType ? GetGenericArguments(collType).SingleOrDefault() ?? typeof(object) : typeof(object);
var mi = collType.GetMethod(nameof(ICollection<object>.Add), new[] {itemType});
if (mi != null)
{
var inst = Expression.Parameter(typeof(object), "collection");
var item = Expression.Parameter(typeof(object), "item");
var itemArgument = itemType == item.Type ? item : Expression.Convert(item, itemType) as Expression; // Cast the item only if itemType is not object
adder = Expression.Lambda<AddItemToCollection>(
Expression.Call(Expression.Convert(inst, collType), mi, itemArgument),
inst,
item).Compile();
}
_genericCollectionAdders[collType] = adder;
return adder;
}

public Type GetGenericType(Type openType, params Type[] genericArguments)
{
Type result;
var key = new GenericTypeKey(openType, genericArguments);
if (_genericTypes.TryGetValue(key, out result))
return result;
var args = key.GetParameters();
System.Diagnostics.Debug.Assert(openType.GetGenericArguments().Length == args.Length);
result = openType.MakeGenericType(args);
_genericTypes.Add(key, result);
return result;
}

public Type GetGenericTypeDefinition(Type t)
{
Type tt = null;
Expand All @@ -221,12 +320,12 @@ public Type GetGenericTypeDefinition(Type t)
public Type[] GetGenericArguments(Type t)
{
Type[] tt = null;
if (_genericTypes.TryGetValue(t, out tt))
if (_genericArguments.TryGetValue(t, out tt))
return tt;
else
{
tt = t.GetGenericArguments();
_genericTypes.Add(t, tt);
_genericArguments.Add(t, tt);
return tt;
}
}
Expand Down Expand Up @@ -851,8 +950,9 @@ internal void ClearReflectionCache()
_constrcache = new SafeDictionary<Type, CreateObject>(10);
_getterscache = new SafeDictionary<Type, Getters[]>(10);
_propertycache = new SafeDictionary<string, Dictionary<string, myPropInfo>>(10);
_genericTypes = new SafeDictionary<Type, Type[]>(10);
_genericArguments = new SafeDictionary<Type, Type[]>(10);
_genericTypeDef = new SafeDictionary<Type, Type>(10);
_genericTypes = new SafeDictionary<GenericTypeKey, Type>(10);
}
}
}