Objects having NonSerialized attribute are not created when deserializing

前端 未结 1 1860
别那么骄傲
别那么骄傲 2021-01-16 16:18

Using the BinaryFormatter class:


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

     
    Publ         


        
1条回答
  •  臣服心动
    2021-01-16 16:25

    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 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 or attributes to a class method (internal(friend)/protected)

    Implementing IDeserializationCallback:

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

    
    Class Class1
        Implements IDeserializationCallback
    
        
        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 attribute:

    When the 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 attribute is applied to List3's backing field:
    (as a note, the [field: NonSerialized] attribute has been added to Properties in c# 7.3)

    
    Class Class1
    
        
        Public List2 As List(Of Integer) = Nothing
    
        
        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
    
        
        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)
    

    0 讨论(0)
提交回复
热议问题