gpt4 book ai didi

c# - 使用附加属性序列化自定义集合并添加到连接项 propertyChanged 事件

转载 作者:太空宇宙 更新时间:2023-11-03 13:15:08 24 4
gpt4 key购买 nike

我有一个自定义集合,我想用 JSON.NET 对其进行序列化:

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

在反序列化时,我需要为集合中的项目连接 PropertyChanged 事件。

如果我按原样传递我的集合,Json 会看到 IEnumerable 并正确序列化集合中的项目,但会忽略其中的其他集合。

如果我使用 [JsonObject] 属性集合,它将序列化所有内部集合,但不序列化内部 _list;

如果我将 [JsonProperty] 添加到内部 _list,它将序列化所有集合。

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

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

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

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

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":"An error has occurred.","ExceptionMessage":"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json; charset=utf-8'.","ExceptionType":"System.InvalidOperationException","StackTrace":null,"InnerException":{"Message":"An error has occurred.","ExceptionMessage":"Token PropertyName in state Property would result in an invalid JSON object. Path '[0]'.","ExceptionType":"Newtonsoft.Json.JsonWriterException","StackTrace":" at Newtonsoft.Json.JsonWriter.AutoComplete(JsonToken tokenBeingWritten)\r\n at Newtonsoft.Json.JsonWriter.InternalWritePropertyName(String name)\r\n at Newtonsoft.Json.JsonTextWriter.WritePropertyName(String name, Boolean escape)\r\n at Newtonsoft.Json.Serialization.JsonProperty.WritePropertyName(JsonWriter writer)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)\r\n at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)\r\n at Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)\r\n at Newtonsoft.Json.JsonSerializer.Serialize(JsonWriter jsonWriter, Object value)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n at System.Net.Http.Formatting.JsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, Encoding effectiveEncoding)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStream(Type type, Object value, Stream writeStream, HttpContent content)\r\n at System.Net.Http.Formatting.BaseJsonMediaTypeFormatter.WriteToStreamAsync(Type type, Object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken)\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.GetResult()\r\n at 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 的定义,这是一个快速原型(prototype)包装器 IList<T>对于 List<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 功能。更多 here .

关于c# - 使用附加属性序列化自定义集合并添加到连接项 propertyChanged 事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26681390/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com