【问题标题】:Deserializing self-tracking entity with nested TrackableCollection使用嵌套的 TrackableCollection 反序列化自跟踪实体
【发布时间】:2010-10-13 12:31:11
【问题描述】:

如何将 JSON 字符串反序列化为这样类型的实体(为了简单起见,删除了自跟踪属性):

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

Role 也是具有两个属性的简单类。 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"}]}

获取具有正确反序列化角色集合的实体。

【问题讨论】:

    标签: json serialization self-tracking-entities


    【解决方案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;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-07-07
      • 2018-09-19
      • 2015-11-07
      • 1970-01-01
      • 1970-01-01
      • 2015-06-07
      • 2014-07-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多