使用嵌套的TrackableCollection反序列化自我跟踪实体

时间:2010-10-13 12:31:12

标签: json serialization self-tracking-entities

如何将JSON字符串反序列化为类似这样的实体(自我跟踪属性被删除为简单):

public class User: 
{
    int Id { get; set; }
    string Name { get; set; }
    public TrackableCollection<Role> Roles { get; set; } // <!
}

角色也是具有两个属性的简单类。 TrackableCollection是Collection(System.Collections.ObjectModel)的后代。

所以我想:拥有像这样的JSON字符串

{"Id":0, "Name":"Test User", "Roles": [{"Id":1, "Name": "Role 1"}, {"Id":2, "Name": "Role 2"}, {"Id":3, "Name": "Role 3"}]}

使用正确反序列化的Roles集合获取实体。

1 个答案:

答案 0 :(得分:1)

好的,似乎没有人对此问题感兴趣,无论如何这里是解决方案。此类序列化和反序列化自跟踪POCO实体,包括所有嵌套的TrackableCollections和对象。

请注意 SupportedTypes 方法。我添加了 IEntity 界面(内部空白)并在此行修改了我的T4模板:

<#=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

你对IEntity无能为力。只需根据需要编写 SupportedTypes 方法。

我还评论了ChangeTracker属性上方模板中的 [DataMember] 属性:

//[DataMember]
public ObjectChangeTracker ChangeTracker

无论如何这并不重要。拿EntityConverter享受。

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