Objects having NonSerialized attribute are not created when deserializing

生来就可爱ヽ(ⅴ<●) 提交于 2020-02-21 06:42:47

问题


Using the BinaryFormatter class:

<Serializable>
Class Class1
    Public Property List1 as New List(Of Something)

    <NonSerialized> 
    Public Property List2 as New List(Of Something)
End Class

when I serialize an object like this one and then deserialize it, List2 will be set to Nothing.

What is the correct way to handle this?
I don't want List2 to be serialized, but I do want it to be an empty list when I deserialize the class.


回答1:


BinaryFormatter doesn't call a class Constuctor, nor it initializes the class object in any way: the opposite, it creates the object non-initialized, in memory.
You can see this in the .Net Source code: when the ParseObject() method is called, the class Object is generated by the GetUninitializedObject(Type type) method.
The next call is made to a [MethodImpl] function, nativeGetUninitializedObject((RuntimeType)type), so we have to stop here, but it's already quite clear what happened: the returned Object represents a non-initialized type, thus none of the non-serialized fields have been initialized nor the class constructor has been called.

If you don't actually need to use the BinaryFormatter class, Json.Net serializer/deserializer does initialize a class object instance when deserializing the type. It also respects the <NonSerialized> attribute. If you decide to use it, you don't need to modify the class object definition.

If you must use a BinaryFormatter, you have two options:

  • Implement the IDeserializationCallback Interface and its OnDeserialization() method
  • Add the <OnDeserializing> or <OnDeserialized> attributes to a class method (internal(friend)/protected)

Implementing IDeserializationCallback:

<NonSerialized> applies to fields: the List2 Auto property is changed into an Instance Field (the Property version is preserved in the next example):

<Serializable>
Class Class1
    Implements IDeserializationCallback

    <NonSerialized>
    Public List2 As List(Of Integer) = New List(Of Integer)()

    Public Property List1 As New List(Of String)

    Public Sub OnDeserialization(sender As Object) Implements IDeserializationCallback.OnDeserialization
        List2 = New List(Of Integer)
    End Sub
End Class

Using the <OnDeserializing> attribute:

When the <OnDeserializing> attribute is added to a method, we don't need to implement the IDeserializationCallback Interface.

Here, a new standard Property with a backing field is added to the Class type.
The <NonSerialized> attribute is applied to List3's backing field:
(as a note, the [field: NonSerialized] attribute has been added to Properties in c# 7.3)

<Serializable>
Class Class1

    <NonSerialized>
    Public List2 As List(Of Integer) = Nothing

    <NonSerialized>
    Private m_List3 As List(Of Double)

    Public Sub New()
        List2 = New List(Of Integer)
    End Sub

    Public Property List1 As New List(Of String)

    Public Property List3 As List(Of Double)
        Get
            Return m_List3
        End Get
        Set
            m_List3 = Value
        End Set
    End Property

    <OnDeserializing()>
    Friend Sub OnDeserialization(ByVal context As StreamingContext)
        List2 = New List(Of Integer)()
        m_List3 = New List(Of Double)()
    End Sub
End Class

In both cases, the BinaryFormatter's Deserialize() method will recreate the serialized class object with the non-serialized Lists initialized but empty:

Dim formatter = New BinaryFormatter()

Dim cls1 = New Class1() With {
    .List1 = New List(Of String) From {"1", "2", "3"},
    .List2 = New List(Of Integer) From {4, 5, 6}
}

Using writer = New FileStream(Path.Combine(AppContext.BaseDirectory(), 
    "Class1Serialized.bin"), FileMode.Create, FileAccess.Write)
    formatter.Serialize(writer, cls1)
End Using

Dim cls1Deserialized As Class1 = Nothing
Using reader = New FileStream(Path.Combine(AppContext.BaseDirectory(), 
    "Class1Serialized.bin"), FileMode.Open, FileAccess.Read)
    cls1Deserialized = TryCast(formatter.Deserialize(reader), Class1)
End Using

Using NewtonSoft.Json:

Dim cls1 = New Class1() With {
    .List1 = New List(Of String) From {"1", "2", "3"},
    .List2 = New List(Of Integer) From {4, 5, 6}
}

Dim class1Json = JsonConvert.SerializeObject(cls1)
Dim class1 = JsonConvert.DeserializeObject(Of Class1)(class1Json)


来源:https://stackoverflow.com/questions/59482504/objects-having-nonserialized-attribute-are-not-created-when-deserializing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!