I've got a generic dictionary Dictionary<string, T> that I would like to essentially make a Clone() of ..any suggestions.
(Note: although the cloning version is potentially useful, for a simple shallow copy the constructor I mention in the other post is a better option.)
How deep do you want the copy to be, and what version of .NET are you using? I suspect that a LINQ call to ToDictionary, specifying both the key and element selector, will be the easiest way to go if you're using .NET 3.5.
For instance, if you don't mind the value being a shallow clone:
var newDictionary = oldDictionary.ToDictionary(entry => entry.Key,
entry => entry.Value);
If you've already constrained T to implement ICloneable:
var newDictionary = oldDictionary.ToDictionary(entry => entry.Key,
entry => (T) entry.Value.Clone());
(Those are untested, but should work.)
Okay, the .NET 2.0 answers:
If you don't need to clone the values, you can use the constructor overload to Dictionary which takes an existing IDictionary. (You can specify the comparer as the existing dictionary's comparer, too.)
If you do need to clone the values, you can use something like this:
public static Dictionary<TKey, TValue> CloneDictionaryCloningValues<TKey, TValue>
(Dictionary<TKey, TValue> original) where TValue : ICloneable
{
Dictionary<TKey, TValue> ret = new Dictionary<TKey, TValue>(original.Count,
original.Comparer);
foreach (KeyValuePair<TKey, TValue> entry in original)
{
ret.Add(entry.Key, (TValue) entry.Value.Clone());
}
return ret;
}
That relies on TValue.Clone() being a suitably deep clone as well, of course.
Dictionary<string, int> dictionary = new Dictionary<string, int>();
Dictionary<string, int> copy = new Dictionary<string, int>(dictionary);
That's what helped me, when I was trying to deep copy a Dictionary < string, string >
Dictionary<string, string> dict2 = new Dictionary<string, string>(dict);
Good luck
For .NET 2.0 you could implement a class which inherits from Dictionary and implements ICloneable.
public class CloneableDictionary<TKey, TValue> : Dictionary<TKey, TValue> where TValue : ICloneable
{
public IDictionary<TKey, TValue> Clone()
{
CloneableDictionary<TKey, TValue> clone = new CloneableDictionary<TKey, TValue>();
foreach (KeyValuePair<TKey, TValue> pair in this)
{
clone.Add(pair.Key, (TValue)pair.Value.Clone());
}
return clone;
}
}
You can then clone the dictionary simply by calling the Clone method. Of course this implementation requires that the value type of the dictionary implements ICloneable, but otherwise a generic implementation isn't practical at all.
This works fine for me
// assuming this fills the List
List<Dictionary<string, string>> obj = this.getData();
List<Dictionary<string, string>> objCopy = new List<Dictionary<string, string>>(obj);
As Tomer Wolberg describes in the comments, this does not work if the value type is a mutable class.
You could always use serialization. You could serialize the object then deserialize it. That will give you a deep copy of the Dictionary and all the items inside of it. Now you can create a deep copy of any object that is marked as [Serializable] without writing any special code.
Here are two methods that will use Binary Serialization. If you use these methods you simply call
object deepcopy = FromBinary(ToBinary(yourDictionary));
public Byte[] ToBinary()
{
MemoryStream ms = null;
Byte[] byteArray = null;
try
{
BinaryFormatter serializer = new BinaryFormatter();
ms = new MemoryStream();
serializer.Serialize(ms, this);
byteArray = ms.ToArray();
}
catch (Exception unexpected)
{
Trace.Fail(unexpected.Message);
throw;
}
finally
{
if (ms != null)
ms.Close();
}
return byteArray;
}
public object FromBinary(Byte[] buffer)
{
MemoryStream ms = null;
object deserializedObject = null;
try
{
BinaryFormatter serializer = new BinaryFormatter();
ms = new MemoryStream();
ms.Write(buffer, 0, buffer.Length);
ms.Position = 0;
deserializedObject = serializer.Deserialize(ms);
}
finally
{
if (ms != null)
ms.Close();
}
return deserializedObject;
}
The best way for me is this:
Dictionary<int, int> copy= new Dictionary<int, int>(yourListOrDictionary);
Binary Serialization method works fine but in my tests it showed to be 10x slower than a non-serialization implementation of clone. Tested it on Dictionary<string , List<double>>
Try this if key/values are ICloneable:
public static Dictionary<K,V> CloneDictionary<K,V>(Dictionary<K,V> dict) where K : ICloneable where V : ICloneable
{
Dictionary<K, V> newDict = null;
if (dict != null)
{
// If the key and value are value types, just use copy constructor.
if (((typeof(K).IsValueType || typeof(K) == typeof(string)) &&
(typeof(V).IsValueType) || typeof(V) == typeof(string)))
{
newDict = new Dictionary<K, V>(dict);
}
else // prepare to clone key or value or both
{
newDict = new Dictionary<K, V>();
foreach (KeyValuePair<K, V> kvp in dict)
{
K key;
if (typeof(K).IsValueType || typeof(K) == typeof(string))
{
key = kvp.Key;
}
else
{
key = (K)kvp.Key.Clone();
}
V value;
if (typeof(V).IsValueType || typeof(V) == typeof(string))
{
value = kvp.Value;
}
else
{
value = (V)kvp.Value.Clone();
}
newDict[key] = value;
}
}
}
return newDict;
}
In the case you have a Dictionary of "object" and object can be anything like (double, int, ... or ComplexClass):
Dictionary<string, object> dictSrc { get; set; }
public class ComplexClass : ICloneable
{
private Point3D ...;
private Vector3D ....;
[...]
public object Clone()
{
ComplexClass clone = new ComplexClass();
clone = (ComplexClass)this.MemberwiseClone();
return clone;
}
}
dictSrc["toto"] = new ComplexClass()
dictSrc["tata"] = 12.3
...
dictDest = dictSrc.ToDictionary(entry => entry.Key,
entry => ((entry.Value is ICloneable) ? (entry.Value as ICloneable).Clone() : entry.Value) );
Here is some real "true deep copying" without knowing type with some recursive walk, good for the beginnig. It is good for nested types and almost any tricky type I think. I did not added nested arrays handling yet, but you can modify it by your choice.
Dictionary<string, Dictionary<string, dynamic>> buildInfoDict =
new Dictionary<string, Dictionary<string, dynamic>>()
{
{"tag",new Dictionary<string,dynamic>(){
{ "attrName", "tag" },
{ "isCss", "False" },
{ "turnedOn","True" },
{ "tag",null }
} },
{"id",new Dictionary<string,dynamic>(){
{ "attrName", "id" },
{ "isCss", "False" },
{ "turnedOn","True" },
{ "id",null }
} },
{"width",new Dictionary<string,dynamic>(){
{ "attrName", "width" },
{ "isCss", "True" },
{ "turnedOn","True" },
{ "width","20%" }
} },
{"height",new Dictionary<string,dynamic>(){
{ "attrName", "height" },
{ "isCss", "True" },
{ "turnedOn","True" },
{ "height","20%" }
} },
{"text",new Dictionary<string,dynamic>(){
{ "attrName", null },
{ "isCss", "False" },
{ "turnedOn","True" },
{ "text","" }
} },
{"href",new Dictionary<string,dynamic>(){
{ "attrName", null },
{ "isCss", "False" },
{ "flags", "removeAttrIfTurnedOff" },
{ "turnedOn","True" },
{ "href","about:blank" }
} }
};
var cln=clone(buildInfoDict);
public static dynamic clone(dynamic obj)
{
dynamic cloneObj = null;
if (IsAssignableFrom(obj, typeof(IDictionary)))
{
cloneObj = Activator.CreateInstance(obj.GetType());
foreach (var key in obj.Keys)
{
cloneObj[key] = clone(obj[key]);
}
}
else if (IsNumber(obj) || obj.GetType() == typeof(string))
{
cloneObj = obj;
}
else
{
Debugger.Break();
}
return cloneObj;
}
public static bool IsAssignableFrom(this object obj, Type ObjType = null, Type ListType = null, bool HandleBaseTypes = false)
{
if (ObjType == null)
{
ObjType = obj.GetType();
}
bool Res;
do
{
Res = (ObjType.IsGenericType && ObjType.GetGenericTypeDefinition().IsAssignableFrom(ListType)) ||
(ListType == null && ObjType.IsAssignableFrom(obj.GetType()));
ObjType = ObjType.BaseType;
} while ((!Res && ObjType != null) && HandleBaseTypes && ObjType != typeof(object));
return Res;
}
public static bool IsNumber(this object value)
{
return value is sbyte
|| value is byte
|| value is short
|| value is ushort
|| value is int
|| value is uint
|| value is long
|| value is ulong
|| value is float
|| value is double
|| value is decimal;
}
Here is another way to clone a dictionary, assuming you know to do the "right" thing as far as handling whatever is hiding behind the "T" (a.k.a. "object") in your specific circumstances.
internal static Dictionary<string, object> Clone(Dictionary<string, object> dictIn)
{
Dictionary<string, object> dictOut = new Dictionary<string, object>();
IDictionaryEnumerator enumMyDictionary = dictIn.GetEnumerator();
while (enumMyDictionary.MoveNext())
{
string strKey = (string)enumMyDictionary.Key;
object oValue = enumMyDictionary.Value;
dictOut.Add(strKey, oValue);
}
return dictOut;
}
I would evaluate if T was a value or reference type. In the case T was a value type I would use the constructor of Dictionary, and in the case when T was a reference type I would make sure T inherited from ICloneable.
It will give
private static IDictionary<string, T> Copy<T>(this IDictionary<string, T> dict)
where T : ICloneable
{
if (typeof(T).IsValueType)
{
return new Dictionary<string, T>(dict);
}
else
{
var copy = new Dictionary<string, T>();
foreach (var pair in dict)
{
copy[pair.Key] = pair.Value;
}
return copy;
}
}
Related
Hi I had an issue while deserializing a json file which can have different data type( float and float array). I got a suggestion to use custom converter from Deserialize a json field with different data types without using Newtonsoft json but with System.Web.Script.Serialization.JavaScriptSerializer and it works fine for one dimensional array. I am currently being given jsons with two dimensional array and this converter couldn't handle the same. Below is a snippet from my json
{
"name" : "foo",
"value" : 457,
"comment" : "bla bla bla",
"data" : [
{
"name" : "bar",
"max" : 200,
"default" : [
[7,4],[2,2],[7,4],[1,1],[2,3],[3,1],[7,9]
]
}
]
}
Where the default can be sometimes primitive like
"default" : 3.56
Edit : Code used for custom serialization
class RootObjectConverter : CustomPropertiesConverter<RootObject>
{
const string ValuesName = "values";
protected override IEnumerable<string> CustomProperties
{
get { return new[] { ValuesName }; }
}
protected override void DeserializeCustomProperties(Dictionary<string, object> customDictionary, RootObject obj, JavaScriptSerializer serializer)
{
object itemCost;
if (customDictionary.TryGetValue(ValuesName, out itemCost) && itemCost != null)
obj.Values = serializer.FromSingleOrArray<float>(itemCost).ToArray();
}
protected override void SerializeCustomProperties(RootObject obj, Dictionary<string, object> dict, JavaScriptSerializer serializer)
{
obj.Values.ToSingleOrArray(dict, ValuesName);
}
}
public abstract class CustomPropertiesConverter<T> : JavaScriptConverter
{
protected abstract IEnumerable<string> CustomProperties { get; }
protected abstract void DeserializeCustomProperties(Dictionary<string, object> customDictionary, T obj, JavaScriptSerializer serializer);
protected abstract void SerializeCustomProperties(T obj, Dictionary<string, object> dict, JavaScriptSerializer serializer);
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
// Detach custom properties
var customDictionary = new Dictionary<string, object>();
foreach (var key in CustomProperties)
{
object value;
if (dictionary.TryRemoveInvariant(key, out value))
customDictionary.Add(key, value);
}
// Deserialize and populate all members other than "values"
var obj = new JavaScriptSerializer().ConvertToType<T>(dictionary);
// Populate custom properties
DeserializeCustomProperties(customDictionary, obj, serializer);
return obj;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
// Generate a default serialization. Is there an easier way to do this?
var defaultSerializer = new JavaScriptSerializer();
var dict = defaultSerializer.Deserialize<Dictionary<string, object>>(defaultSerializer.Serialize(obj));
// Remove default serializations of custom properties, if present
foreach (var key in CustomProperties)
{
dict.RemoveInvariant(key);
}
// Add custom properties
SerializeCustomProperties((T)obj, dict, serializer);
return dict;
}
public override IEnumerable<Type> SupportedTypes
{
get { return new[] { typeof(T) }; }
}
}
public static class JavaScriptSerializerObjectExtensions
{
public static void ReplaceInvariant<T>(this IDictionary<string, T> dictionary, string key, T value)
{
RemoveInvariant(dictionary, key);
dictionary.Add(key, value);
}
public static bool TryRemoveInvariant<T>(this IDictionary<string, T> dictionary, string key, out T value)
{
if (dictionary == null)
throw new ArgumentNullException();
var keys = dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray();
if (keys.Length == 0)
{
value = default(T);
return false;
}
else if (keys.Length == 1)
{
value = dictionary[keys[0]];
dictionary.Remove(keys[0]);
return true;
}
else
{
throw new ArgumentException(string.Format("Duplicate keys found: {0}", String.Join(",", keys)));
}
}
public static void RemoveInvariant<T>(this IDictionary<string, T> dictionary, string key)
{
if (dictionary == null)
throw new ArgumentNullException();
foreach (var actualKey in dictionary.Keys.Where(k => string.Equals(k, key, StringComparison.OrdinalIgnoreCase)).ToArray())
dictionary.Remove(actualKey);
}
public static void ToSingleOrArray<T>(this ICollection<T> list, IDictionary<string, object> dictionary, string key)
{
if (dictionary == null)
throw new ArgumentNullException();
if (list == null || list.Count == 0)
dictionary.RemoveInvariant(key);
else if (list.Count == 1)
dictionary.ReplaceInvariant(key, list.First());
else
dictionary.ReplaceInvariant(key, list.ToArray());
}
public static List<T> FromSingleOrArray<T>(this JavaScriptSerializer serializer, object value)
{
if (value == null)
return null;
if (value.IsJsonArray())
{
return value.AsJsonArray().Select(i => serializer.ConvertToType<T>(i)).ToList();
}
else
{
return new List<T> { serializer.ConvertToType<T>(value) };
}
}
public static bool IsJsonArray(this object obj)
{
if (obj is string || obj is IDictionary)
return false;
return obj is IEnumerable;
}
public static IEnumerable<object> AsJsonArray(this object obj)
{
return (obj as IEnumerable).Cast<object>();
}
}
and used it like
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new RootObjectConverter() });
var root = serializer.Deserialize<RootObject>(json);
Can anyone help me on how to proceed on this?
I fixed the issue by using a Dictionary instead of a class object for parsing different data type fields and accessing the respective property with the key value. Thanks Everyone for your suggestions.
My objective:
To create a list of objects with dynamically generated properties which can be passed to WCF service parameter as a JSON array.
Final outcome is the following:
[{id:2, name:"John", country:"Germany"},...] or
[{id:3, city:"Sydney"},...]
From WCF I don't have the convenience of working with a class, i.e. "Thing" so I could do this:
public List<Thing> MyThings {get; set;}
I do not know what the properties are at run time. I have experimented with System.Dynamic namespace using
List<ExpandoObject>
Unfortunately, dynamic code will give me the error message:
Dynamic operations can only be performed in homogenous AppDomain.
To allow dynamic code to run I would have to change legacyCasModel tag in the web.config. Unfortunately I cannot change the web.config.
Another option is to use Dictionary array but I do not want the key/value pair format:
[{key:id}, {value:2}]
for I'm interested in
[{id:2, name:"John"}]
My challenge is that I don't know what the properties are at run time otherwise it would be easy to convert a List<T> to array.
Any suggestion?
To pass list of dynamically generated collection as JSON array to WCF web service, you can use ExpandoObject and any serializer, I have used Newtonsoft.Json
dynamic obj1 = new ExpandoObject();
obj1.id = 1;
obj1.name = "TPS";
dynamic obj2 = new ExpandoObject();
obj2.id = 2;
obj2.name = "TPS_TPS";
var Objects = new List<ExpandoObject>();
Objects.Add(flexible);
Objects.Add(flexible2);
var serialized = JsonConvert.SerializeObject(l); // JsonConvert - from Newtonsoft.Json
To Deserialize and retrieve the object dynamically i have used JavaScriptSerializer, you can also use System.Web.Helpers.Json.Decode()
All you need is this code and reference to a System.Web.Extensions from your project :
public sealed class DynamicJsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
return type == typeof(object) ? new DynamicJsonObject(dictionary) : null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
throw new NotImplementedException();
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type>(new[] { typeof(object) })); }
}
#region Nested type: DynamicJsonObject
private sealed class DynamicJsonObject : DynamicObject
{
public readonly IDictionary<string, object> _dictionary;
public DynamicJsonObject(IDictionary<string, object> dictionary)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
_dictionary = dictionary;
}
public override string ToString()
{
var sb = new StringBuilder("{");
ToString(sb);
return sb.ToString();
}
private void ToString(StringBuilder sb)
{
var firstInDictionary = true;
foreach (var pair in _dictionary)
{
if (!firstInDictionary)
sb.Append(",");
firstInDictionary = false;
var value = pair.Value;
var name = pair.Key;
if (value is string)
{
sb.AppendFormat("{0}:\"{1}\"", name, value);
}
else if (value is IDictionary<string, object>)
{
new DynamicJsonObject((IDictionary<string, object>)value).ToString(sb);
}
else if (value is ArrayList)
{
sb.Append(name + ":[");
var firstInArray = true;
foreach (var arrayValue in (ArrayList)value)
{
if (!firstInArray)
sb.Append(",");
firstInArray = false;
if (arrayValue is IDictionary<string, object>)
new DynamicJsonObject((IDictionary<string, object>)arrayValue).ToString(sb);
else if (arrayValue is string)
sb.AppendFormat("\"{0}\"", arrayValue);
else
sb.AppendFormat("{0}", arrayValue);
}
sb.Append("]");
}
else
{
sb.AppendFormat("{0}:{1}", name, value);
}
}
sb.Append("}");
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!_dictionary.TryGetValue(binder.Name, out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
if (indexes.Length == 1 && indexes[0] != null)
{
if (!_dictionary.TryGetValue(indexes[0].ToString(), out result))
{
// return null to avoid exception. caller can check for null this way...
result = null;
return true;
}
result = WrapResultObject(result);
return true;
}
return base.TryGetIndex(binder, indexes, out result);
}
private static object WrapResultObject(object result)
{
var dictionary = result as IDictionary<string, object>;
if (dictionary != null)
return new DynamicJsonObject(dictionary);
var arrayList = result as ArrayList;
if (arrayList != null && arrayList.Count > 0)
{
return arrayList[0] is IDictionary<string, object>
? new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x)))
: new List<object>(arrayList.Cast<object>());
}
return result;
}
}
#endregion
}
How to Use :
string jsonString = ".......";
var serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new[] { new DynamicJsonConverter() });
dynamic data = serializer.Deserialize(jsonString, typeof(object));
foreach (var d in data)
{
var id = d.id;
var name = d.name;
}
Are there any elegant quick way to map object to a dictionary and vice versa?
Example:
IDictionary<string,object> a = new Dictionary<string,object>();
a["Id"]=1;
a["Name"]="Ahmad";
// .....
becomes
SomeClass b = new SomeClass();
b.Id=1;
b.Name="Ahmad";
// ..........
Using some reflection and generics in two extension methods you can achieve that.
Right, others did mostly the same solution, but this uses less reflection which is more performance-wise and way more readable:
public static class ObjectExtensions
{
public static T ToObject<T>(this IDictionary<string, object> source)
where T : class, new()
{
var someObject = new T();
var someObjectType = someObject.GetType();
foreach (var item in source)
{
someObjectType
.GetProperty(item.Key)
.SetValue(someObject, item.Value, null);
}
return someObject;
}
public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
{
return source.GetType().GetProperties(bindingAttr).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null)
);
}
}
class A
{
public string Prop1
{
get;
set;
}
public int Prop2
{
get;
set;
}
}
class Program
{
static void Main(string[] args)
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
dictionary.Add("Prop1", "hello world!");
dictionary.Add("Prop2", 3893);
A someObject = dictionary.ToObject<A>();
IDictionary<string, object> objectBackToDictionary = someObject.AsDictionary();
}
}
Convert the Dictionary to JSON string first with Newtonsoft.
var json = JsonConvert.SerializeObject(advancedSettingsDictionary, Newtonsoft.Json.Formatting.Indented);
Then deserialize the JSON string to your object
var myobject = JsonConvert.DeserializeObject<AOCAdvancedSettings>(json);
Seems reflection only help here.. I've done small example of converting object to dictionary and vise versa:
[TestMethod]
public void DictionaryTest()
{
var item = new SomeCLass { Id = "1", Name = "name1" };
IDictionary<string, object> dict = ObjectToDictionary<SomeCLass>(item);
var obj = ObjectFromDictionary<SomeCLass>(dict);
}
private T ObjectFromDictionary<T>(IDictionary<string, object> dict)
where T : class
{
Type type = typeof(T);
T result = (T)Activator.CreateInstance(type);
foreach (var item in dict)
{
type.GetProperty(item.Key).SetValue(result, item.Value, null);
}
return result;
}
private IDictionary<string, object> ObjectToDictionary<T>(T item)
where T: class
{
Type myObjectType = item.GetType();
IDictionary<string, object> dict = new Dictionary<string, object>();
var indexer = new object[0];
PropertyInfo[] properties = myObjectType.GetProperties();
foreach (var info in properties)
{
var value = info.GetValue(item, indexer);
dict.Add(info.Name, value);
}
return dict;
}
I'd highly recommend the Castle DictionaryAdapter, easily one of that project's best-kept secrets. You only need to define an interface with the properties you want, and in one line of code the adapter will generate an implementation, instantiate it, and synchronize its values with a dictionary you pass in. I use it to strongly-type my AppSettings in a web project:
var appSettings =
new DictionaryAdapterFactory().GetAdapter<IAppSettings>(ConfigurationManager.AppSettings);
Note that I did not need to create a class that implements IAppSettings - the adapter does that on the fly. Also, although in this case I'm only reading, in theory if I were setting property values on appSettings, the adapter would keep the underlying dictionary in sync with those changes.
I think you should use reflection. Something like this:
private T ConvertDictionaryTo<T>(IDictionary<string, object> dictionary) where T : new()
{
Type type = typeof (T);
T ret = new T();
foreach (var keyValue in dictionary)
{
type.GetProperty(keyValue.Key).SetValue(ret, keyValue.Value, null);
}
return ret;
}
It takes your dictionary and loops through it and sets the values. You should make it better but it's a start. You should call it like this:
SomeClass someClass = ConvertDictionaryTo<SomeClass>(a);
Reflection can take you from an object to a dictionary by iterating over the properties.
To go the other way, you'll have to use a dynamic ExpandoObject (which, in fact, already inherits from IDictionary, and so has done this for you) in C#, unless you can infer the type from the collection of entries in the dictionary somehow.
So, if you're in .NET 4.0 land, use an ExpandoObject, otherwise you've got a lot of work to do...
Building on MatÃas Fidemraizer's answer, here is a version that supports binding to object properties other than strings.
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace WebOpsApi.Shared.Helpers
{
public static class MappingExtension
{
public static T ToObject<T>(this IDictionary<string, object> source)
where T : class, new()
{
var someObject = new T();
var someObjectType = someObject.GetType();
foreach (var item in source)
{
var key = char.ToUpper(item.Key[0]) + item.Key.Substring(1);
var targetProperty = someObjectType.GetProperty(key);
//edited this line
if (targetProperty.PropertyType == item.Value.GetType())
{
targetProperty.SetValue(someObject, item.Value);
}
else
{
var parseMethod = targetProperty.PropertyType.GetMethod("TryParse",
BindingFlags.Public | BindingFlags.Static, null,
new[] {typeof (string), targetProperty.PropertyType.MakeByRefType()}, null);
if (parseMethod != null)
{
var parameters = new[] { item.Value, null };
var success = (bool)parseMethod.Invoke(null, parameters);
if (success)
{
targetProperty.SetValue(someObject, parameters[1]);
}
}
}
}
return someObject;
}
public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
{
return source.GetType().GetProperties(bindingAttr).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null)
);
}
}
}
public class SimpleObjectDictionaryMapper<TObject>
{
public static TObject GetObject(IDictionary<string, object> d)
{
PropertyInfo[] props = typeof(TObject).GetProperties();
TObject res = Activator.CreateInstance<TObject>();
for (int i = 0; i < props.Length; i++)
{
if (props[i].CanWrite && d.ContainsKey(props[i].Name))
{
props[i].SetValue(res, d[props[i].Name], null);
}
}
return res;
}
public static IDictionary<string, object> GetDictionary(TObject o)
{
IDictionary<string, object> res = new Dictionary<string, object>();
PropertyInfo[] props = typeof(TObject).GetProperties();
for (int i = 0; i < props.Length; i++)
{
if (props[i].CanRead)
{
res.Add(props[i].Name, props[i].GetValue(o, null));
}
}
return res;
}
}
If you are using Asp.Net MVC, then take a look at:
public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes);
which is a static public method on the System.Web.Mvc.HtmlHelper class.
public Dictionary<string, object> ToDictionary<T>(string key, T value)
{
try
{
var payload = new Dictionary<string, object>
{
{ key, value }
};
} catch (Exception e)
{
return null;
}
}
public T FromDictionary<T>(Dictionary<string, object> payload, string key)
{
try
{
JObject jObject = (JObject) payload[key];
T t = jObject.ToObject<T>();
return (t);
}
catch(Exception e) {
return default(T);
}
}
I want to compare in C# two dictionaries with as keys a string and as value a list of ints. I assume two dictionaries to be equal when they both have the same keys and for each key as value a list with the same integers (both not necessarily in the same order).
I use both the answers from this and this related question, but both fail my test suite for the test functions DoesOrderKeysMatter and DoesOrderValuesMatter.
My test suite:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;
namespace UnitTestProject1
{
[TestClass]
public class ProvideReportTests
{
[TestMethod]
public void AreSameDictionariesEqual()
{
// arrange
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
dict1.Add("a", list1);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
dict1.Add("b", list2);
// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1, dict1);
// assert
Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");
}
[TestMethod]
public void AreDifferentDictionariesNotEqual()
{
// arrange
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
dict1.Add("a", list1);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
dict1.Add("b", list2);
Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();
// act
bool dictsAreEqual = true;
dictsAreEqual = AreDictionariesEqual(dict1, dict2);
// assert
Assert.IsFalse(dictsAreEqual, "Dictionaries are equal");
}
[TestMethod]
public void DoesOrderKeysMatter()
{
// arrange
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
dict1.Add("a", list1);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
dict1.Add("b", list2);
Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();
List<int> list3 = new List<int>();
list3.Add(3);
list3.Add(4);
dict2.Add("b", list3);
List<int> list4 = new List<int>();
list4.Add(1);
list4.Add(2);
dict2.Add("a", list4);
// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1, dict2);
// assert
Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");
}
[TestMethod]
public void DoesOrderValuesMatter()
{
// arrange
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>();
List<int> list1 = new List<int>();
list1.Add(1);
list1.Add(2);
dict1.Add("a", list1);
List<int> list2 = new List<int>();
list2.Add(3);
list2.Add(4);
dict1.Add("b", list2);
Dictionary<string, List<int>> dict2 = new Dictionary<string, List<int>>();
List<int> list3 = new List<int>();
list3.Add(2);
list3.Add(1);
dict2.Add("a", list3);
List<int> list4 = new List<int>();
list4.Add(4);
list4.Add(3);
dict2.Add("b", list4);
// act
bool dictsAreEqual = false;
dictsAreEqual = AreDictionariesEqual(dict1, dict2);
// assert
Assert.IsTrue(dictsAreEqual, "Dictionaries are not equal");
}
private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
{
return dict1.Keys.Count == dict2.Keys.Count &&
dict1.Keys.All(k => dict2.ContainsKey(k) && object.Equals(dict2[k], dict1[k]));
// also fails:
// return dict1.OrderBy(kvp => kvp.Key).SequenceEqual(dict2.OrderBy(kvp => kvp.Key));
}
}
}
What is the correct way to compare these kind of dictionaries? Or is there an error in my (admittedly clumsily written) TestSuite?
Update
I'm trying to incorporate Servy's answer in my test suite, like below, but I get some errors (underlined with a red wiggly line in Visual Studio):
SetEquals in the `Equals method says: "does not contain a definition for SetEquals accepting a first argument of type Generic.List.
In AreDictionariesEqualit saysDictionaryComparer<List> is a type but is used as a variable.`
namespace UnitTestProject1
{
[TestClass]
public class ProvideReportTests
{
[TestMethod]
// ... same as above
private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
{
DictionaryComparer<string, List<int>>(new ListComparer<int>() dc = new DictionaryComparer<string, List<int>>(new ListComparer<int>();
return dc.Equals(dict1, dict2);
}
}
public class DictionaryComparer<TKey, TValue> :
IEqualityComparer<Dictionary<TKey, TValue>>
{
private IEqualityComparer<TValue> valueComparer;
public DictionaryComparer(IEqualityComparer<TValue> valueComparer = null)
{
this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
}
public bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
{
if (x.Count != y.Count)
return false;
if (x.Keys.Except(y.Keys).Any())
return false;
if (y.Keys.Except(x.Keys).Any())
return false;
foreach (var pair in x)
if (!valueComparer.Equals(pair.Value, y[pair.Key]))
return false;
return true;
}
public int GetHashCode(Dictionary<TKey, TValue> obj)
{
throw new NotImplementedException();
}
}
public class ListComparer<T> : IEqualityComparer<List<T>>
{
private IEqualityComparer<T> valueComparer;
public ListComparer(IEqualityComparer<T> valueComparer = null)
{
this.valueComparer = valueComparer ?? EqualityComparer<T>.Default;
}
public bool Equals(List<T> x, List<T> y)
{
return x.SetEquals(y, valueComparer);
}
public int GetHashCode(List<T> obj)
{
throw new NotImplementedException();
}
}
public static bool SetEquals<T>(this IEnumerable<T> first, IEnumerable<T> second, IEqualityComparer<T> comparer)
{
return new HashSet<T>(second, comparer ?? EqualityComparer<T>.Default)
.SetEquals(first);
}
}
So first we need an equality comparer for dictionaries. It needs to ensure that they have matching keys and, if they do, compare the values of each key:
public class DictionaryComparer<TKey, TValue> :
IEqualityComparer<Dictionary<TKey, TValue>>
{
private IEqualityComparer<TValue> valueComparer;
public DictionaryComparer(IEqualityComparer<TValue> valueComparer = null)
{
this.valueComparer = valueComparer ?? EqualityComparer<TValue>.Default;
}
public bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y)
{
if (x.Count != y.Count)
return false;
if (x.Keys.Except(y.Keys).Any())
return false;
if (y.Keys.Except(x.Keys).Any())
return false;
foreach (var pair in x)
if (!valueComparer.Equals(pair.Value, y[pair.Key]))
return false;
return true;
}
public int GetHashCode(Dictionary<TKey, TValue> obj)
{
throw new NotImplementedException();
}
}
but this isn't enough on its own. We need to compare the values of the dictionary using another custom comparer, not the default comparer as the default list comparer won't look at the values of the list:
public class ListComparer<T> : IEqualityComparer<List<T>>
{
private IEqualityComparer<T> valueComparer;
public ListComparer(IEqualityComparer<T> valueComparer = null)
{
this.valueComparer = valueComparer ?? EqualityComparer<T>.Default;
}
public bool Equals(List<T> x, List<T> y)
{
return x.SetEquals(y, valueComparer);
}
public int GetHashCode(List<T> obj)
{
throw new NotImplementedException();
}
}
Which uses the following extension method:
public static bool SetEquals<T>(this IEnumerable<T> first, IEnumerable<T> second,
IEqualityComparer<T> comparer)
{
return new HashSet<T>(second, comparer ?? EqualityComparer<T>.Default)
.SetEquals(first);
}
Now we can simply write:
new DictionaryComparer<string, List<int>>(new ListComparer<int>())
.Equals(dict1, dict2);
I know this question already has an accepted answer, but I'd like to offer an even simpler alternative:
using System.Linq;
using System.Collections.Generic;
namespace Foo
{
public static class DictionaryExtensionMethods
{
public static bool ContentEquals<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, Dictionary<TKey, TValue> otherDictionary)
{
return (otherDictionary ?? new Dictionary<TKey, TValue>())
.OrderBy(kvp => kvp.Key)
.SequenceEqual((dictionary ?? new Dictionary<TKey, TValue>())
.OrderBy(kvp => kvp.Key));
}
}
}
Convert the dictionary to a KeyValuePair list and then compare as collections:
CollectionAssert.AreEqual(
dict1.OrderBy(kv => kv.Key).ToList(),
dict2.OrderBy(kv => kv.Key).ToList()
);
I think that AreDictionariesEqual() just needs another method for List comparison
So if order of entries doesn't matter you can try this:
static bool ListEquals(List<int> L1, List<int> L2)
{
if (L1.Count != L2.Count)
return false;
return L1.Except(L2).Count() == 0;
}
/*
if it is ok to change List content you may try
L1.Sort();
L2.Sort();
return L1.SequenceEqual(L2);
*/
static bool DictEquals(Dictionary<string, List<int>> D1, Dictionary<string, List<int>> D2)
{
if (D1.Count != D2.Count)
return false;
return D1.Keys.All(k => D2.ContainsKey(k) && ListEquals(D1[k],D2[k]));
}
And if order of entries matters, try this:
static bool DictEqualsOrderM(Dictionary<string, List<int>> D1, Dictionary<string, List<int>> D2)
{
if (D1.Count != D2.Count)
return false;
//check keys for equality, than lists.
return (D1.Keys.SequenceEqual(D2.Keys) && D1.Keys.All(k => D1[k].SequenceEqual(D2[k])));
}
The accepted answer above will not always return a correct comparison because
using a HashSet to compare 2 lists will not account for duplicate values in the lists.
For instance if the OP had:
var dict1 = new Dictionary<string, List<int>>() { { "A", new List<int>() { 1, 2, 1 } } };
var dict2 = new Dictionary<string, List<int>>() { { "A", new List<int>() { 2, 2, 1 } } };
Then the result of the dictionary comparison is they are equal, when they are not. The only solution I see is to sort the 2 list and compare the values by index, but I'm sure someone smarter then me can come up with a more efficient way.
Here is a way using Linq, probably sacrificing some efficiency for tidy code. The other Linq example from jfren484 actually fails the DoesOrderValuesMatter() test, because it depends on the default Equals() for List<int>, which is order-dependent.
private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
{
string dict1string = String.Join(",", dict1.OrderBy(kv => kv.Key).Select(kv => kv.Key + ":" + String.Join("|", kv.Value.OrderBy(v => v))));
string dict2string = String.Join(",", dict2.OrderBy(kv => kv.Key).Select(kv => kv.Key + ":" + String.Join("|", kv.Value.OrderBy(v => v))));
return dict1string.Equals(dict2string);
}
If two dictionaries are known to use equivalent implementations of IEqualityComparer, and one wishes to regard as equivalent all keys which that implementation regardss as equivalent, they contain the same number of items, and one (arbitrarily chosen) maps all of the elements keys found in the other to corresponding values from the other, they will be equivalent unless or until one of them is modified. Testing for those conditions will be faster than any approach which does not not assume that both dictionaries use the same IEqualityComparer.
If two dictionaries do not use the same implementation of IEqualityComparer, they should generally not be considered equivalent regardless of the items they contain. For example, a Dictionary<String,String> with a case-sensitive comparer and one with a case-insensitive comparer, both of which contain the key-value pair ("Fred", "Quimby") are not equivalent, since the latter would map "FRED" to "Quimby", but the former would not.
Only if the dictionaries use the same implementation of IEqualityComparer, but if one is interested in a finer-grained definition of key-equality than the one used by the dictionaries and a copy of the key is not stored with each value, it will it be necessary to build a new dictionary for the purpose of testing the original dictionaries for equality. It may be best to delay this step until the earlier test has suggested that the dictionaries seem to match. Then build a Dictionary<TKey,TKey> which maps each key from one of the dictionaries to itself, and then look up all of the other dictionary's keys in that to make sure that they map to things which match. If both dictionaries used case-insensitive comparers, and one contained ("Fred", "Quimby") and the other ("FRED", "Quimby"), the new temporary dictionary would map "FRED" to "Fred", and comparing those two strings would reveal that the dictionaries don't match.
Most of the answers are iterating the dictionaries multiple times while it should be simple:
static bool AreEqual(IDictionary<string, string> thisItems, IDictionary<string, string> otherItems)
{
if (thisItems.Count != otherItems.Count)
{
return false;
}
var thisKeys = thisItems.Keys;
foreach (var key in thisKeys)
{
if (!(otherItems.TryGetValue(key, out var value) &&
string.Equals(thisItems[key], value, StringComparison.OrdinalIgnoreCase)))
{
return false;
}
}
return true;
}
I like this approach because it gives more details when the test fails
public void AssertSameDictionary<TKey,TValue>(Dictionary<TKey,TValue> expected,Dictionary<TKey,TValue> actual)
{
string d1 = "expected";
string d2 = "actual";
Dictionary<TKey,TValue>.KeyCollection keys1= expected.Keys;
Dictionary<TKey,TValue>.KeyCollection keys2= actual.Keys;
if (actual.Keys.Count > expected.Keys.Count)
{
string tmp = d1;
d1 = d2;
d2 = tmp;
Dictionary<TKey, TValue>.KeyCollection tmpkeys = keys1;
keys1 = keys2;
keys2 = tmpkeys;
}
foreach(TKey key in keys1)
{
Assert.IsTrue(keys2.Contains(key), $"key '{key}' of {d1} dict was not found in {d2}");
}
foreach (TKey key in expected.Keys)
{
//already ensured they both have the same keys
Assert.AreEqual(expected[key], actual[key], $"for key '{key}'");
}
}
public static IDictionary<string, object> ToDictionary(this object source)
{
var fields = source.GetType().GetFields(
BindingFlags.GetField |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source) ?? string.Empty
);
var properties = source.GetType().GetProperties(
BindingFlags.GetField |
BindingFlags.GetProperty |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null) ?? string.Empty
);
return fields.Concat(properties).ToDictionary(key => key.Key, value => value.Value); ;
}
public static bool EqualsByValue(this object source, object destination)
{
var firstDic = source.ToFlattenDictionary();
var secondDic = destination.ToFlattenDictionary();
if (firstDic.Count != secondDic.Count)
return false;
if (firstDic.Keys.Except(secondDic.Keys).Any())
return false;
if (secondDic.Keys.Except(firstDic.Keys).Any())
return false;
return firstDic.All(pair =>
pair.Value.ToString().Equals(secondDic[pair.Key].ToString())
);
}
public static bool IsAnonymousType(this object instance)
{
if (instance == null)
return false;
return instance.GetType().Namespace == null;
}
public static IDictionary<string, object> ToFlattenDictionary(this object source, string parentPropertyKey = null, IDictionary<string, object> parentPropertyValue = null)
{
var propsDic = parentPropertyValue ?? new Dictionary<string, object>();
foreach (var item in source.ToDictionary())
{
var key = string.IsNullOrEmpty(parentPropertyKey) ? item.Key : $"{parentPropertyKey}.{item.Key}";
if (item.Value.IsAnonymousType())
return item.Value.ToFlattenDictionary(key, propsDic);
else
propsDic.Add(key, item.Value);
}
return propsDic;
}
Comparing dictionary using string keys is way more complex than what it looks at first glance.
Dictionary<TKey,TValue> uses an IEqualityComparer<TKey> every time you access an entry in the dictionary to compare your input with the actual entries. The comparer is also used for hash calculations, which serves as some kind of index for faster random access to the entries. Trying to compare dictionaries with different comparers may have some side effects on key sorting and equality considerations for the key-value pair. The key point here is you need to compare the comparers too when comparing dictionaries.
Dictionary<TKey,TValue> also provides collections of keys and values, but they are unsorted. The keys and values collections are consistent inside the dictionary (the nth key is the nth value's key), but not across instances. This means we'll have to work with KeyValuePairs<TKey,TValue> and sort them by key on both dictionaries before comparing them.
However, the comparers in the dictionary only check for equality, it's not able to sort the keys. In order to sort pairs, we'll need a new IComparer<TKey> instance, which is another interface than IEqualityComparer<TKey>. But there's a trap here: default implementations of these two interfaces are not consistent. When you create a dictionary using the default contructor, the class will instanciate a GenericEqualityComparer<TKey> if TKey implements IEquatable<TKey>, which require TKey to implement bool Equals(TKey other); (otherwise, it will fallback to an ObjectEqualityComparer). If you create default Comparer, that will instanciate a GenericComparer<TKey> if TKey implements IComparable<TKey>, which will require TKey to implement int CompareTo(TKey other); (otherwise it will default to an ObjectComparer). Not all types implement both interfaces, and those who do sometimes use different implementations. There is a risk that two different keys (according to Equals) are sorted identically (according to CompareTo). In that case, there's a risk on the key sorting consitency.
Fortunately, string implements both interfaces. Unfortunately, its implementations are NOT consistent: CompareTo depends on the current culture to sort items, whereas Equals does not ! The solution to this problem is to inject a custom comparer to the dictionary, which provides consistent implementation of both interface. We can use StringComparer for that rather than relying on the default implementation. Then we'll simply get the dictionary comparer, cast it, and use it for sorting keys. Also, StringComparer allows comparing the comparers, so we can ensure both dictionaries use the same one.
First, we need a way to compare the values of the dictionary. Since you want to compare lists of int without order, we'll implement an generic equality comparer that sorts items and SequenceEqual them.
internal class OrderInsensitiveListComparer<TValue>
: IEqualityComparer<IEnumerable<TValue>>
{
private readonly IComparer<TValue> comparer;
public OrderInsensitiveListComparer(IComparer<TValue> comparer = null)
{
this.comparer = comparer ?? Comparer<TValue>.Default;
}
public bool Equals([AllowNull] IEnumerable<TValue> x, [AllowNull] IEnumerable<TValue> y)
{
return x != null
&& y != null
&& Enumerable.SequenceEqual(
x.OrderBy(value => value, comparer),
y.OrderBy(value => value, comparer));
}
public int GetHashCode([DisallowNull] IEnumerable<TValue> obj)
{
return obj.Aggregate(17, (hash, item) => hash * 23 ^ item.GetHashCode());
}
}
Now, we've got the values covered, but we also need to compare KeyValuePair. It is a simple ref struct, so we don't need to check for nulls. We'll simply delegate the comparison to two comparers : one for the key, another for the value.
internal class KeyValuePairComparer<TKey, TValue> : IEqualityComparer<KeyValuePair<TKey, TValue>>
{
private readonly IEqualityComparer<TKey> key;
private readonly IEqualityComparer<TValue> value;
public KeyValuePairComparer(
IEqualityComparer<TKey> key = null,
IEqualityComparer<TValue> value = null)
{
this.key = key ?? EqualityComparer<TKey>.Default;
this.value = value ?? EqualityComparer<TValue>.Default;
}
public bool Equals([AllowNull] KeyValuePair<TKey, TValue> x, [AllowNull] KeyValuePair<TKey, TValue> y)
{
// KeyValuePair is a struct, you can't null check
return key.Equals(x.Key, y.Key) && value.Equals(x.Value, y.Value);
}
public int GetHashCode([DisallowNull] KeyValuePair<TKey, TValue> obj)
{
return 17 * 23 ^ obj.Key.GetHashCode() * 23 ^ obj.Value.GetHashCode();
}
}
Now, we can implement the dictionary comparer. We do null check and compare the dictionaries comparers. Then we consider the dictionary as a simple enumerable of KeyValuePair and SequenceEqual them after sorting them by key. For that, we cast the dictionary comparer and delegate the comparison to the KeyValueComparer.
internal class DictionaryComparer<TValue> : IEqualityComparer<Dictionary<string, TValue>>
{
private readonly IEqualityComparer<TValue> comparer;
public DictionaryComparer(
IEqualityComparer<TValue> comparer = null)
{
this.comparer = comparer ?? EqualityComparer<TValue>.Default;
}
public bool Equals([AllowNull] Dictionary<string, TValue> x, [AllowNull] Dictionary<string, TValue> y)
{
return x != null
&& y != null
&& Equals(x.Comparer, y.Comparer)
&& x.Comparer is StringComparer sorter
&& Enumerable.SequenceEqual(
x.AsEnumerable().OrderBy(pair => pair.Key, sorter),
y.AsEnumerable().OrderBy(pair => pair.Key, sorter),
new KeyValuePairComparer<string, TValue>(x.Comparer, comparer));
}
public int GetHashCode([DisallowNull] Dictionary<string, TValue> obj)
{
return new OrderInsensitiveListComparer<KeyValuePair<string, TValue>>()
.GetHashCode(obj.AsEnumerable()) * 23 ^ obj.Comparer.GetHashCode();
}
}
Finally, we only need to instanciate comparers and let them do the work.
private bool AreDictionariesEqual(Dictionary<string, List<int>> dict1, Dictionary<string, List<int>> dict2)
{
return new DictionaryComparer<List<int>>(
new OrderInsensitiveListComparer<int>())
.Equals(dict1, dict2);
}
However, for this to work, we need to use a StringComparer in every dictionary.
[TestMethod]
public void DoesOrderValuesMatter()
{
Dictionary<string, List<int>> dict1 = new Dictionary<string, List<int>>(StringComparer.CurrentCulture);
// more stuff
}
The function shown below can be accommodated to perform any generic comparison:
public bool AreDictionaryEquals(Dictionary<ulong?, string> dictionaryList1, Dictionary<ulong?, string> dictionaryList2)
{
if (dictionaryList1.Count != dictionaryList2.Count)
return false;
IDictionary<ulong?, string> orderedList1 = new Dictionary<ulong?, string>();
IDictionary<ulong?, string> orderedList2 = new Dictionary<ulong?, string>();
foreach (var itemDict1 in dictionaryList1.OrderByDescending(key => key.Id))
{
orderedList1.Add(itemDict1.Id, itemDict1.PropertyX);
}
foreach (var itemDict2 in dictionaryList2.OrderByDescending(key => key.Id))
{
orderedList2.Add(itemDict2.Id, itemDict2.PropertyX);
}
//check keys and values for equality
return (orderedList1.Keys.SequenceEqual(orderedList2.Keys) && orderedList1.Keys.All(k => orderedList1[k].SequenceEqual(orderedList2[k])));
}
1- If the length of both dictionaries is not equal we can safely return false.
2- Then, we proceed to sort both dictionaries using the value of the keys. The reason for doing this is you could have situations like this:
Dictionary A: [1,A], [3,B]
Dictionary B: [3,B], [1,A]
Even though the order is not the same, the content of both can be considered equal.
Finally:
3- We compare both sorted sequences and retrieve the result of this comparison.
I'm looking for more generic/"standard" way to instantiate object of some type T from set of pairs string,object. For me it looks like there should be some well known way to do it but I cannot find it, so I come up with this piece of code.
Does anybody know something better?
// usage
public class test
{
public int field1;
public string field2;
public bool field3;
public string[] field4;
public IDictionary<string,object> field5 { get; set; }
public static IDictionary<string,object> dynamic()
{
return new Dictionary<string,object>{
{ "field1", 2 },
{ "field2", "string" },
{ "field3", true },
{ "field4", new[] { "id3", "id4", "id5" } },
{ "field5", new Dictionary<string,object>{ { "id1", "" } } }
};
}
}
...
var r = new dynamic_data_serializer<test>().create( test.dynamic() );
...
//
public class dynamic_data_serializer< T >
{
public T create( object obj )
{
var result = default(T);
if ( obj == null )
return result;
var ttype = typeof(T);
var objtype = obj.GetType();
if ( ttype.IsAssignableFrom( objtype ) ) {
result = (T)obj;
return result;
}
if ( ttype.IsClass ) { // custom classes, array, dictionary, etc.
result = Activator.CreateInstance<T>();
if ( objtype == typeof(IDictionary<string,object>) ||
objtype == typeof(Dictionary<string,object>) ) {
var obj_as_dict = obj as IDictionary<string,object>;
var fields = ttype.GetFields();
if ( fields.Length > 0 )
set_fields_from( result, fields, obj_as_dict );
var properties = ttype.GetProperties();
if ( properties.Length > 0 )
set_properties_from( result, properties, obj_as_dict );
}
}
return result;
}
private void set_fields_from( T _this_, FieldInfo[] fields, IDictionary<string,object> obj ) {
foreach ( var fld in fields ) {
var v = find( obj, fld.Name );
if ( v != null ) {
var mobj = call_deserialize( fld.FieldType, v );
fld.SetValue( _this_, mobj );
}
}
}
private void set_properties_from( T _this_, PropertyInfo[] properties, IDictionary<string,object> obj ) {
foreach ( var prop in properties ) {
var v = find( obj, prop.Name );
if ( v != null ) {
var mobj = call_deserialize( prop.PropertyType, v );
prop.SetValue( _this_, mobj, null );
}
}
}
private object find( IDictionary<string,object> obj, string name ) {
foreach ( var kv in obj )
if ( string.Compare( kv.Key, name, true ) == 0 )
return kv.Value;
return null;
}
private object call_deserialize( Type des_type, object value ) {
var gtype = typeof(dynamic_data_serializer<>);
Type desz_type = gtype.MakeGenericType( new[]{ des_type } );
object desz = Activator.CreateInstance( desz_type );
var method_type = desz_type.GetMethod( "create" );
return method_type.Invoke( desz, new[]{ value } );
}
}
}
DataContractJsonSerializer is too slow, but you're using reflection? If you have to deserialize lots of objects, I would recommend using compiled lambdas instead of reflection. A lambda can only set properties, not fields (at least in .Net 3.5), so you may have to adjust the classes you use it on, but it's worth it because it's like 1000 times faster.
Here's a function that creates a property setter given a type and a PropertyInfo for the property to set:
static Action<object, TValue> MakeSetter<TValue>(Type tclass, PropertyInfo propInfo)
{
var t = lambda.Expression.Parameter(typeof(object), "t");
var v = lambda.Expression.Parameter(typeof(TValue), "v");
// return (t, v) => ((tclass)t).prop = (tproperty)v
return (Action<object, TValue>)
lambda.Expression.Lambda(
lambda.Expression.Call(
lambda.Expression.Convert(t, tclass),
propInfo.GetSetMethod(),
lambda.Expression.Convert(v, propInfo.PropertyType)),
t,
v)
.Compile();
}
You would have a dictionary of setters for each class, and whenever you have to set a property of a class, you would look up the setter for that property in the dictionary and call it with the value to assign, like this: setters[propName](_this_, value);
I might suggest FormatterServices.PopulateObjectMembers, except a: this is still slow AFAIK, and b: I tried it (below) and it seems to want to throw an exception on the property (don't know why; didn't look too deep). Another option may be Expression, but you don't really want to do the Compile each time (better to do it once only and cache it, but that demands a known format).
public T create(object obj)
{ // simplified for illustration
var bindings = obj as IDictionary<string, object>;
Type type = typeof(T);
var func = Expression.Lambda<Func<T>>(Expression.MemberInit(
Expression.New(type),
from pair in bindings
let member = type.GetMember(pair.Key).Single()
select (MemberBinding)Expression.Bind(member, Expression.Constant(pair.Value))));
return func.Compile().Invoke();
}
Finally, you might cache a set of pre-compiled Action<object> setters (keyed against the member name). In reality this is probably your best bet. Properties are easy (you use Delegate.CreateDelegate) - fields might need DynamicMethod - but if you can't predict the layout in advance it'll have the least overhead.
For the keyed / IL approach (you won't get faster):
public class dynamic_data_serializer<T>
{
public T create(object obj)
{
T inst = Activator.CreateInstance<T>();
var bindings = obj as IDictionary<string, object>;
foreach (var pair in bindings)
{
setters[pair.Key](inst, pair.Value);
}
return inst;
}
private static readonly Dictionary<string, Action<T, object>> setters;
static dynamic_data_serializer()
{
setters = new Dictionary<string, Action<T, object>>(StringComparer.Ordinal);
foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
setters.Add(prop.Name, CreateForMember(prop));
}
foreach (FieldInfo field in typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance)) {
setters.Add(field.Name, CreateForMember(field));
}
}
static Action<T, object> CreateForMember(MemberInfo member)
{
bool isField;
Type type;
switch (member.MemberType) {
case MemberTypes.Property:
isField = false;
type = ((PropertyInfo)member).PropertyType;
break;
case MemberTypes.Field:
isField = true;
type = ((FieldInfo)member).FieldType;
break;
default:
throw new NotSupportedException();
}
DynamicMethod method = new DynamicMethod("__set_" + member.Name, null, new Type[] { typeof(T), typeof(object) });
ILGenerator il = method.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
if(type != typeof(object)) {
il.Emit(type.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, type);
}
if (isField) {il.Emit(OpCodes.Stfld, (FieldInfo)member);}
else { il.EmitCall(OpCodes.Callvirt, ((PropertyInfo)member).GetSetMethod(), null); }
il.Emit(OpCodes.Ret);
return (Action<T, object>)method.CreateDelegate(typeof(Action<T, object>));
}
}
DataContractJsonSerializer
Why would you make a custom serializer and not use the DataContractJsonSerializer?
EDIT
If DataContractJsonSerializer doesn't fit you, you can try JSON.Net. Implementing a serializer efficiently is not an easy task, there's a lot of pitfalls and special cases that you may not want to get into. By the way, your code sample makes heavy use of reflection which is slow, I doubt that it will perform better than DataContractJsonSerializer or JSON.Net.