开发者

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;
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜