Deserializing self-tracking entity with nested TrackableCollection
How can I deserialize JSON string into entity of type like this (self-tracking properties removed to be simple):
public class User:
{
int Id { get; set; }
string Name { get; set; }
public TrackableCollection<Role> Roles { get; set; } // <!
}
Role is also simple class with two properties. Tracka开发者_如何学CbleCollection is descendant of Collection (System.Collections.ObjectModel).
So I want: having JSON string like this
{"Id":0, "Name":"Test User", "Roles": [{"Id":1, "Name": "Role 1"}, {"Id":2, "Name": "Role 2"}, {"Id":3, "Name": "Role 3"}]}
get entity with correctly deserialized Roles collection.
Ok, seems like nobody was interested this question, anyway here is the solution. This class serializes and deserializes self-tracking POCO entities including all nested TrackableCollections and objects.
Please notice about SupportedTypes method. I added the IEntity interface (blank inside) and modified my T4 template on this line:
<#=Accessibility.ForType(entity)#> <#=code.SpaceAfter(code.AbstractOption(entity))#>partial class <#=code.Escape(entity)#><#=code.StringBefore(" : ", code.Escape(entity.BaseType))#><#=(entity.BaseType == null ? ": " : ", ") + "IEntity" #>, IObjectWithChangeTracker, INotifyPropertyChanged
You can do nothing about IEntity. Just write the SupportedTypes method as you need.
I also commented the [DataMember] attribute in the template above the ChangeTracker property:
//[DataMember]
public ObjectChangeTracker ChangeTracker
Anyway this is not important. Take the EntityConverter and enjoy.
/// <summary>
/// Serializes self-tracking POCO entities with DataMemberAttribute marked properties.
/// </summary>
public class EntityConverter : JavaScriptConverter
{
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string, object> result = new Dictionary<string, object>();
if (obj != null)
{
var properties = obj.GetType().GetProperties().Where(prop => prop.HasAttibute(typeof(DataMemberAttribute)));
foreach (var property in properties)
{
object value = property.GetValue(obj, null);
// Serialize nested TrackableCollection object
if (property.PropertyType.Name.Equals(typeof(TrackableCollection<object>).Name))
value = SerializeCollection((value as IEnumerable).Cast<object>(), serializer);
result.Add(property.Name, value);
}
}
return result;
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
throw new ArgumentNullException("dictionary");
var entity = Activator.CreateInstance(type);
foreach (KeyValuePair<string, object> kvp in dictionary)
{
PropertyInfo property = type.GetProperty(kvp.Key);
if ((property != null) && (property.HasAttibute(typeof(DataMemberAttribute))))
{
object value = default(object);
if (!property.PropertyType.Name.Equals(typeof(TrackableCollection<object>).Name))
{
// If property is not a TrackableCollection object
// http://stackoverflow.com/questions/793714/how-can-i-fix-this-up-to-do-generic-conversion-to-nullablet
Type u = Nullable.GetUnderlyingType(property.PropertyType);
string jsonValue = kvp.Value != null ? kvp.Value.ToString() : null;
dynamic dynamicVal;
if (u != null)
dynamicVal = jsonValue == "null" ? null : Convert.ChangeType(jsonValue, u);
else if (kvp.Value is IDictionary<string, object>)
dynamicVal = Deserialize(kvp.Value as IDictionary<string, object>, property.PropertyType, serializer);
else
dynamicVal = Convert.ChangeType(jsonValue, property.PropertyType);
value = dynamicVal;
}
else
{
// If property is a TrackableCollection object
var dictionaries = (kvp.Value as IEnumerable).Cast<IDictionary<string, object>>();
value = DeserializeCollection(dictionaries, property.PropertyType, serializer);
}
property.SetValue(entity, value, null);
}
}
return entity;
}
/// <summary>
/// Serializes TrackableCollection
/// </summary>
protected IList<IDictionary<string, object>> SerializeCollection(IEnumerable<object> collection, JavaScriptSerializer serializer)
{
var result = new List<IDictionary<string, object>>();
foreach (object obj in collection)
{
result.Add(Serialize(obj, serializer));
}
return result;
}
/// <summary>
/// Deserializes TrackableCollection
/// </summary>
protected object DeserializeCollection(IEnumerable<IDictionary<string, object>> dictionaries, Type propertyType, JavaScriptSerializer serializer)
{
object collection = Activator.CreateInstance(propertyType); // TrackableCollection<T>
Type genericType = propertyType.GetGenericArguments()[0]; // T
MethodInfo addMethod = collection.GetType().GetMethod("Add"); // Add(T object)
foreach (var dic in dictionaries)
{
addMethod.Invoke(collection, new [] { Deserialize(dic, genericType, serializer) });
}
return collection;
}
/// <remarks>
/// http://stackoverflow.com/questions/159704/how-to-implement-custom-json-serialization-from-asp-net-web-service
/// </remarks>
public override IEnumerable<Type> SupportedTypes
{
get
{
IList<Type> result = new List<Type>();
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
AssemblyBuilder dynamicAssemblyCheck = assembly as AssemblyBuilder;
if (dynamicAssemblyCheck == null)
{
try
{
foreach (Type type in assembly.GetExportedTypes())
{
if ((type != typeof(IEntity)) && typeof(IEntity).IsAssignableFrom(type))
{
result.Add(type);
}
}
}
catch(Exception){} // bad practice, i know, i know
}
}
return result;
}
}
}
精彩评论