json - Newtonsoft JSON - 如何反序列化自定义集合?
问题描述
我尝试使用 Newtonsoft.net 反序列化自定义集合。
Public Class ObservableCollectionAdvanced(Of T As INotifyPropertyChanged)
Implements IEnumerable(Of T)
Implements INotifyPropertyChanged
Implements INotifyCollectionChanged
Implements INotifyCollectionItemPropertyChanged
Private ReadOnly _collection As New ObservableCollection(Of T)
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Event CollectionChanged As NotifyCollectionChangedEventHandler Implements INotifyCollectionChanged.CollectionChanged
Public Event CollectionItemPropertyChanged As PropertyChangedEventHandler Implements INotifyCollectionItemPropertyChanged.CollectionItemPropertyChanged
Public Sub New()
AddHandler _collection.CollectionChanged, Sub(sender As Object, e As NotifyCollectionChangedEventArgs)
RaiseEvent CollectionChanged(Me, e)
End Sub
End Sub
Default Public ReadOnly Property Item(Index As Integer) As T
Get
If Index >= 0 AndAlso Index < _collection.Count Then
Return _collection(Index)
Else
Return Nothing
End If
End Get
End Property
Public Sub Add(Item As T)
If Item IsNot Nothing Then
Me.AddHandlerToItem(Item)
_collection.Add(Item)
End If
End Sub
Private Sub AddHandlerToItem(Item As T)
If Not TypeOf Item Is INotifyPropertyChanged Then Exit Sub
AddHandler DirectCast(Item, INotifyPropertyChanged).PropertyChanged, AddressOf Item_PropertyChanged
End Sub
Private Sub Item_PropertyChanged(sender As Object, e As PropertyChangedEventArgs)
RaiseEvent CollectionItemPropertyChanged(sender, e)
End Sub
Public Function GetEnumerator() As IEnumerator(Of T) Implements IEnumerable(Of T).GetEnumerator
Return _collection.GetEnumerator()
End Function
Private Function GetEnumerator1() As IEnumerator Implements IEnumerable.GetEnumerator
Return Me.GetEnumerator()
End Function
End Class
这是我的实现。在反序列化时,我得到了异常。“无法创建和填充列表类型”。我已经尝试了 ObservableCollection,没有任何例外。
Dim devices As New ObservableCollectionAdvanced(Of IDevice)
devices.add(new TorsionArmDevice())
Dim JsonString as string = JsonConvert.SerializeObject(devices, Newtonsoft.Json.Formatting.Indented, New JsonSerializerSettings With {.TypeNameHandling = TypeNameHandling.All})
Newtonsoft.Json.JsonConvert.DeserializeObject(of ObservableCollectionAdvanced(Of IDevice))(JsonString, New JsonSerializerSettings With {.TypeNameHandling = TypeNameHandling.All})
演示小提琴在这里。
解决方案
您的问题是您ObservableCollectionAdvanced(Of T As INotifyPropertyChanged)
唯一的实现IEnumerable(Of T)
not ICollection(Of T)
,因此 Json.NET 将其视为只读或不可变集合。它只会拾取该Public Sub Add(Item As T)
方法,如果它Implements ICollection(Of T).Add
- 你没有,因为类型本身没有实现ICollection(Of T)
。每当您尝试使用 Json.NET 反序列化(显然)只读集合时,它不知道如何填充,您将收到错误消息
无法创建和填充列表类型
TCollection`1[TItem]
那么你有什么选择呢?
首先,从Json.NET 6.0 第 3 版开始,只要Items As IEnumerable(Of T)
只读集合具有采用单个参数的公共构造函数,Json.NET 就支持反序列化。因此,您可以添加一个:
Public Sub New()
Me.New(Enumerable.Empty(Of T)())
End Sub
Public Sub New(Items As IEnumerable(Of T))
' TODO: Decide whether to add the items before or after binding the event
For Each i in Items
If i IsNot Nothing Then
_collection.Add(i)
End If
Next
AddHandler _collection.CollectionChanged, Sub(sender As Object, e As NotifyCollectionChangedEventArgs)
RaiseEvent CollectionChanged(Me, e)
End Sub
End Sub
现在 Json.NET 可以成功反序列化您的集合。演示小提琴#1在这里。
其次,您可以实施ICollection(Of T)
:
Public Class ObservableCollectionAdvanced(Of T As INotifyPropertyChanged)
Implements ICollection(Of T) ' changed from IEnumerable(Of T)
Implements INotifyPropertyChanged
Implements INotifyCollectionChanged
Implements INotifyCollectionItemPropertyChanged
' Implement ICollection(Of T)
Public Sub Add(Item As T) Implements ICollection(Of T).Add
If Item IsNot Nothing Then
Me.AddHandlerToItem(Item)
_collection.Add(Item)
End If
End Sub
Public Function Contains(ByVal item As T) As Boolean Implements ICollection(Of T).Contains
If item IsNot Nothing Then
Return _collection.COntains(item)
End If
Return False
End Function
Public ReadOnly Property Count() As Integer Implements ICollection(Of T).Count
Get
Return _collection.Count
End Get
End Property
Public ReadOnly Property IsReadOnly() As Boolean Implements ICollection(Of T).IsReadOnly
Get
Return False
End Get
End Property
Public Sub CopyTo(array As T(), arrayIndex As Integer) Implements ICollection(Of T).CopyTo
_collection.CopyTo(array, arrayIndex)
End Sub
Public Sub Clear() Implements ICollection(Of T).Clear
Throw New NotImplementedException()
' Or, add correct notifications and uncomment
' _collection.Clear()
End Sub
Public Function Remove(ByVal item As T) As Boolean Implements ICollection(Of T).Remove
Throw New NotImplementedException()
' Or, add correct notifications and uncomment
' If item Is Nothing Then
' Return False
' End If
' Return _collection.Remove(item)
End Function
' End ICollection(Of T)
' Remainder unchanged
演示小提琴#2在这里。
笔记:
您的问题不包括定义,
INotifyCollectionItemPropertyChanged
因此我无法猜测要在Clear()
和中提出什么事件Remove()
。因此,我在两者中都提出了例外。Json.NET 是用 c# 编写的。在风格上,c# 倾向于通过类型继承和接口实现使用静态编译时绑定,而 VB 从一开始就有后期绑定。这可以解释为什么Json.NET 没有实现
Add(item As T)
在不存在的情况下的发现。Implements ICollection(Of T).Add
顺便说一句,我注意到您正在使用
TypeNameHandling.All
. 这可能会使您面临安全风险,例如Newtonsoft Json 中的 TypeNameHandling 警告中所述的安全风险。为了减轻这些风险,请考虑编写一个自定义Serializationbinder
来验证传入类型。
推荐阅读
- javascript - 如何在不输入所有字母的情况下在 JavaScript 中生成随机 a - z 字母(1 个字母)?
- amazon-web-services - 亚马逊子域上 ALB 的 SSL 证书?
- ios - 关于使用 Keychains 和 Provisioning Profiles Management 的 Jenkins 自动包设置
- spring-boot - kubernetes 上的 Mariadb 读取通信包时出错
- javascript - 无法缩小 javascript 类
- python - Textarea 字段返回字符串字典以在 django 中转换字典
- javascript - 动态 TypeScript 接口上的访问属性
- javascript - 如果 value == null 则省略对象的键值
- php - 按日期求和 Laravel 数据表
- python - 在 Python 中定义一个将第一个数字加倍并将第二个数字加倍的函数