将自定义集合与其他属性串联,并在添加到连接项propertyChanged事件上



我有一个自定义集合,我想将其与JSON.NET:串行化

我需要它来序列化这个自定义集合中的子集合。

在取消序列化时,我需要为集合中的项挂接PropertyChanged事件。

如果我按原样传递集合,Json会看到IEnumerable并串行化集合中的项,但会忽略其中的其他集合。

如果我用[JsonObject]赋予集合属性,它将串行化所有内部集合,而不是内部_list;

如果我将[JsonProperty]添加到内部列表中,它将串行化所有集合。

但是,由于它在反序列化过程中将_list设置为属性,因此我的自定义集合的Add Method不会被调用,因此_list中项目的propertyChanged事件永远不会被连接起来。

我尝试隐藏内部_list并用公共getter setter包装它,我想如果在反序列化过程中它使用公共setter来设置我可以附加到那里的项事件的内部_list,但这也不起作用。

在反序列化过程中,我可以做些什么来连接内部_list中项目的notifyproperty更改事件吗?

编辑:我尝试了一个转换器:

public class TrackableCollectionConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(TrackableCollectionCollection<ITrackableEntity>);
    }
    public override object ReadJson(
        JsonReader reader, Type objectType,
        object existingValue, JsonSerializer serializer)
    {
        // N.B. null handling is missing
        var surrogate = serializer.Deserialize<TrackableCollectionCollection<ITrackableEntity>>(reader);

        var trackableCollection = new TrackableCollectionCollection<ITrackableEntity>();
        foreach (var el in surrogate)
            trackableCollection.Add(el);
        foreach (var el in surrogate.NewItems)
            trackableCollection.NewItems.Add(el);
        foreach (var el in surrogate.ModifiedItems)
            trackableCollection.ModifiedItems.Add(el);
        foreach (var el in surrogate.DeletedItems)
            trackableCollection.DeletedItems.Add(el);
        return trackableCollection;
    }
    public override void WriteJson(JsonWriter writer, object value,
                                   JsonSerializer serializer)
    {
        serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        serializer.Serialize(writer, value);
    }
}

给出错误:

{"Message":"发生错误。","ExceptionMessage":"'ObjectContent`1'类型未能序列化内容类型'application/json;charset=utf-8'的响应正文。","ExceptionType":"System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"发生错误。","ExceptionMessage":"状态Property中的Token PropertyName将导致无效的JSON对象。路径"[0]"。","ExceptionType":"Newtonsoft.Json.JsonWriterException","StackTrace":"位于Newtonsoft.Json.Json writer.AutoComplete(JsonToken标记BeingWritten)\r\n位于Newtonsoft.Json。JsonWriter.InternalWritePropertyName(字符串名称)\r\n位于New tonsoft.J.sonTextWriter.WritePropertyName在Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter编写器,对象值,JsonObjectContract约定,JsonProperty成员,JsonContainerContract集合contract,JsonPropertycontainerProperty)\r\n(JsonWriter编写器,对象值,JsonContract值contract,JsonProperty成员,JsonContainerContract containerContract,JsonProperty containerProperty)\r\n位于Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList\\r\n在Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValueNewtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter JsonWriter,对象值,类型objectTypeSystem.Net.Http.Formating.BaseJsonMediaTypeFormatter.WriteToStream(类型类型、对象值、流写流、编码有效编码)(类型类型类型、对象值、流writeStream、HttpContent内容、TransportContext TransportContext、CancellationToken cancel Token)\r\n-从引发异常的前一位置开始的堆栈跟踪结束---\r\n在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)\r\n处,位于System.Runtime.TompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\r\n在System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n在System.Web.Http.WebHost.HttpControllerHandler.d__1b.MoveNext()"}}

这是我迄今为止的收藏品。

[Serializable]
[JsonObject]
[JsonConverter(typeof(TrackableCollectionConverter))]
public class TrackableCollectionCollection<T> : IList<T> where T : ITrackableEntity
{
    [JsonIgnore]
    IList<T> _list = new List<T>();
    [JsonProperty]
    public IList<T> List
    {
        get { return _list; }
        set 
        { 
            _list = value; 
            foreach(var item in _list)
                item.PropertyChanged += item_PropertyChanged;
        }
    } 
    [DataMember]
    public IList<T> NewItems
    {
        get { return _newItems; }
    }
    IList<T> _newItems = new List<T>();
    [DataMember]
    public IList<T> ModifiedItems
    {
        get { return _modifiedChildren; }
    }
    IList<T> _modifiedChildren = new List<T>();
    [DataMember]
    public IList<T> DeletedItems
    {
        get { return _deletedItems; }
    }
    IList<T> _deletedItems = new List<T>();
    #region Implementation of IEnumerable
    public IEnumerator<T> GetEnumerator()
    {
        return _list.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    #endregion
    #region Implementation of ICollection<T>
    public void Add(T item)
    {
        if (item.Id.Equals(default(Guid)))
            _newItems.Add(item);
        else
        {
            // I thought about doing this but that would screw the EF object generation.
            // throw new NotSupportedException("");
        }
        item.PropertyChanged += item_PropertyChanged;
        _list.Add(item);
    }

    public void Clear()
    {
        NewItems.Clear();
        ModifiedItems.Clear();
        foreach(var item in _list)
        {
            item.PropertyChanged -= item_PropertyChanged;
            DeletedItems.Add(item);
        }
        _list.Clear();
    }
    public bool Contains(T item)
    {
        return _list.Contains(item);
    }
    public void CopyTo(T[] array, int arrayIndex)
    {
        _list.CopyTo(array, arrayIndex);
    }
    public bool Remove(T item)
    {
        if (NewItems.Contains(item))
            NewItems.Remove(item);
        if (ModifiedItems.Contains(item))
            ModifiedItems.Remove(item);
        if (!DeletedItems.Contains(item))
            DeletedItems.Add(item);
        return _list.Remove(item);
    }
    public int Count
    {
        get { return _list.Count; }
    }
    public bool IsReadOnly
    {
        get { return _list.IsReadOnly; }
    }
    #endregion
    #region Implementation of IList<T>
    public int IndexOf(T item)
    {
        return _list.IndexOf(item);
    }
    public void Insert(int index, T item)
    {
        if (item.Id.Equals(default(Guid)))
            _newItems.Add(item);
        else
        {
            // I thought about doing this but that would screw the EF object generation.
            // throw new NotSupportedException("");
        }
        item.PropertyChanged += item_PropertyChanged;
        _list.Insert(index, item);
    }
    public void RemoveAt(int index)
    {
        var item = this[index];
        if (NewItems.Contains(item))
            NewItems.Remove(item);
        if (ModifiedItems.Contains(item))
            ModifiedItems.Remove(item);
        if (!DeletedItems.Contains(item))
            DeletedItems.Add(item);
        _list.RemoveAt(index);
    }
    public T this[int index]
    {
        get { return _list[index]; }
        set { _list[index] = value; }
    }
    #endregion
    void item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (((T)sender).Id.Equals(default(Guid)))
            return; // The Item is already in the newItems collection
        if (ModifiedItems.Contains((T)sender))
            return;
        ModifiedItems.Add((T)sender);
    }
}

您可以像现在这样将自定义容器序列化为JsonObject,并将嵌入列表序列化为代理ObservableCollection<T>。然后,您可以监听代理的添加和删除,并相应地进行处理。注意--不需要自定义JsonConverter。由于我没有你对ITrackableEntity的定义,这里有一个List<T>:的快速原型包装器IList<T>

[Serializable]
[JsonObject]
public class ListContainer<T> : IList<T> 
{
    [JsonIgnore]
    readonly List<T> _list = new List<T>();
    [JsonProperty("List")]
    private IList<T> SerializableList
    {
        get
        {
            var proxy = new ObservableCollection<T>(_list);
            proxy.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(proxy_CollectionChanged);
            return proxy;
        }
        set
        {
            _list.Clear();
            _list.AddRange(value);
        }
    }
    void proxy_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
        {
            foreach (var item in e.NewItems.Cast<T>())
                Add(item);
        }
        else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
        {
            foreach (var item in e.NewItems.Cast<T>())
                Remove(item);
        }
        else
        {
            Debug.Assert(false);
            throw new NotImplementedException();
        }
    }
    [JsonIgnore]
    public int Count
    {
        get { return _list.Count; }
    }
    [JsonIgnore]
    public bool IsReadOnly
    {
        get { return ((IList<T>)_list).IsReadOnly; }
    }
    // Everything beyond here is boilerplate.
    #region IList<T> Members
    public int IndexOf(T item)
    {
        return _list.IndexOf(item);
    }
    public void Insert(int index, T item)
    {
        _list.Insert(index, item);
    }
    public void RemoveAt(int index)
    {
        _list.RemoveAt(index);
    }
    public T this[int index]
    {
        get
        {
            return _list[index];
        }
        set
        {
            _list[index] = value;
        }
    }
    #endregion
    #region ICollection<T> Members
    public void Add(T item)
    {
        _list.Add(item);
    }
    public void Clear()
    {
        _list.Clear();
    }
    public bool Contains(T item)
    {
        return _list.Contains(item);
    }
    public void CopyTo(T[] array, int arrayIndex)
    {
        _list.CopyTo(array, arrayIndex);
    }
    public bool Remove(T item)
    {
        return _list.Remove(item);
    }
    #endregion
    #region IEnumerable<T> Members
    public IEnumerator<T> GetEnumerator()
    {
        return _list.GetEnumerator();
    }
    #endregion
    #region IEnumerable Members
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    #endregion
}

然后,测试:

    public static void TestListContainerJson()
    {
        var list = new ListContainer<int>();
        list.Add(101);
        list.Add(102);
        list.Add(103);
        var json = JsonConvert.SerializeObject(list);
        var newList = JsonConvert.DeserializeObject<ListContainer<int>>(json);
        Debug.Assert(list.SequenceEqual(newList)); // No assert.
    }

更新

事实证明,Json.NET遵循与XmlSerializer相同的模式:如果您将代理列表序列化为数组,则读取后将使用完全填充的数组调用setter,并且您可以根据需要添加它们:

[Serializable]
[JsonObject]
public class ListContainer<T> : IList<T>
{
    [JsonIgnore]
    readonly List<T> _list = new List<T>();
    [JsonProperty("List")]
    private T [] SerializableList
    {
        get
        {
            return _list.ToArray();
        }
        set
        {
            Clear();
            foreach (var item in value)
                Add(item);
        }
    }
    [JsonIgnore]
    public int Count
    {
        get { return _list.Count; }
    }
    [JsonIgnore]
    public bool IsReadOnly
    {
        get { return ((IList<T>)_list).IsReadOnly; }
    }
    // Everything beyond here is boilerplate.
}

这比我的第一个解决方案干净多了。

此外,我怀疑您的NewItemsModifiedItems列表包含对主_list中项目的引用。默认情况下,Json.NET将在序列化期间有效地克隆这些&反序列化。要避免这种情况,请查看PreserveReferencesHandling功能。点击此处了解更多信息。

我解决了我的问题。

第一:[JsonConverter(typeof(TrackableCollectionConverter))]不应该出现在类定义上。除了CCD_ 12保持不变之外。

我修改了我的转换器,因此:

public class TrackableCollectionConverter<TEntity, TDeserialiseType> : JsonConverter where TEntity: ITrackableEntity
{
    public override bool CanConvert(Type objectType)
    {
        return true;
        //return objectType == typeof(TrackableCollectionCollection<ITrackableEntity>);
    }
    public override object ReadJson(
        JsonReader reader, Type objectType,
        object existingValue, JsonSerializer serializer)
    {
        // N.B. null handling is missing
        var surrogate = serializer.Deserialize<TDeserialiseType>(reader) as TrackableCollectionCollection<TEntity>;

        var trackablecollection = new TrackableCollectionCollection<TEntity>();
        foreach (var el in surrogate)
            trackablecollection.Add(el);
        foreach (var el in surrogate.NewItems)
            trackablecollection.NewItems.Add(el);
        foreach (var el in surrogate.ModifiedItems)
            trackablecollection.ModifiedItems.Add(el);
        foreach (var el in surrogate.DeletedItems)
            trackablecollection.DeletedItems.Add(el);
        return trackablecollection;
    }
    public override void WriteJson(JsonWriter writer, object value,
                                   JsonSerializer serializer)
    {
        serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
        serializer.Serialize(writer, value);
    }
}

最后将CustomConverter属性放在正确的位置。在实体属性上。在我的案例中,我有一个名为Parent:的实体

[JsonObject(IsReference = true)]
[DataContract(IsReference = true)]
public class Parent : TrackableEntityBase
{
    [DataMember]
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid ParentId
    {
        get { return base.Id ; }
        set
        {
            if (base.Id.Equals(default(Guid)))
                base.Id = value;
            if (base.Id.Equals(value))
                return;
            throw new InvalidOperationException("Primary Keys cannot be changed once set.");
        }
    }
    [DataMember]
    public String Name 
    {
        get { return _name; }
        set
        {
            if (!String.IsNullOrWhiteSpace(_name) && _name.Equals(value, StringComparison.Ordinal))
            {
                return;
            }
            _name = value;
            OnPropertyChanged("Name");
        }
    }
    String _name;
    [DataMember]
    [JsonConverter(typeof(TrackableCollectionConverter<Child, TrackableCollectionCollection<Child>>))]
    public virtual TrackableCollectionCollection<Child> Children { get; set; }
}

这可以很好地生成这样的json:

{"$id":"1","ParentId":"6d884973-5060-e411-8265-cffad877042b","Name":"Parent1","Children":{"List":[{"$id":"2","ChildId":"5bd66353-3f61-e411-8265-cffad877042b","ParentId":"6d884973-5060-e411-8265-cffad877042b","Name":"Billy","Parent":{"$ref":"1"},"Id":"5bd66353-3f61-e411-8265-cffad877042b","IsModified":true}],"NewItems":[],"ModifiedItems":[{"$ref":"2"}],"DeletedItems":[],"Count":1,"IsReadOnly":false},"Id":"6d884973-5060-e411-8265-cffad877042b","IsModified":true}

相关内容

  • 没有找到相关文章

最新更新