Newtonsoft JSON - 如何反序列化自定义集合?

Newtonsoft JSON - How can I deserialize a custom collection?

我尝试使用 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

这是我的实现。在反序列化时,我得到了 en Exception。 “无法创建和填充列表类型”。我试过 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})

演示 fiddle here.

您的问题是您的 ObservableCollectionAdvanced(Of T As INotifyPropertyChanged) 仅实现 IEnumerable(Of T) 而不是 ICollection(Of T),因此 Json.NET 将其视为只读或不可变集合。如果 Implements ICollection(Of T).Add 它只会在 Public Sub Add(Item As T) 方法上使用 - 你的方法没有,因为类型本身没有实现 ICollection(Of T)。每当您尝试使用 Json.NET 反序列化一个(显然)只读集合时,它不知道如何填充,您将收到错误消息

Cannot create and populate list type TCollection`1[TItem]

那么你有什么选择?

首先,从Json.NET 6.0 Release 3开始Json.NET支持反序列化只读集合只要它们有一个public 构造函数采用单个 Items As IEnumerable(Of T) 参数 。所以,你可以添加一个:

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 可以成功反序列化您的集合。演示 fiddle #1 here.

其次,你可以实现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

演示 fiddle #2 here.

备注:

  • 您的问题不包含 INotifyCollectionItemPropertyChanged 的定义,所以我无法猜测 Clear() and Remove() 中会引发什么事件。因此我在两者中都提出了例外。

  • Json.NET是用c#写的。在风格上,c# 倾向于通过类型继承和接口实现使用静态编译时绑定,而 VB 从一开始就具有延迟绑定。这可以解释为什么 Json.NET.

    没有在没有 Implements ICollection(Of T).Add 的情况下发现 Add(item As T)
  • 顺便说一句,我注意到您正在使用 TypeNameHandling.All 进行序列化。这可能会让您面临安全风险,例如 . To mitigate those risks, consider writing a custom Serializationbinder 中描述的验证传入类型的风险。