VB.Net序列化和反序列化私有成员 - 简单的例子

时间:2014-12-12 13:23:18

标签: .net vb.net object serialization private-members

为了传输/克隆等目的,我需要序列化任何对象(到xml,json,其他 - 不要太在意)。

我发现newtonsoft.json库非常简单,除非它没有序列化私有成员。我在c#中找到了似乎提供我需要使用合同的例子,但我无法让它们起作用。

我将很感激一个简单的例子:

e.g。

 Class Person
     Private _NI as integer
     Public Name as string
End Class

我需要两个功能:

Function SerializePerson(P as person) as string

Function DeSerializePerson(SerializedText as string) as Person

理想情况下,我想要一种易于复制任何对象的方法。

3 个答案:

答案 0 :(得分:4)

Newtonsoft的诀窍是更改JsonSerializerSettings并将其传递给JsonConvert.SerializeObject。此答案包含此更改的完整详细信息:https://stackoverflow.com/a/24107081/2019162

修改

我没想到链接代码在C#中,因为我经常使用它们。我很抱歉。我已经翻译了以下代码:

Dim settings As New JsonSerializerSettings() With {.ContractResolver = New MyContractResolver()}
Dim json As String = JsonConvert.SerializeObject(obj, settings)

合同解决方案:

Imports System.Reflection
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Serialization

Public Class MyContractResolver
    Inherits DefaultContractResolver
    Protected Overrides Function CreateProperties(type As Type, memberSerialization As MemberSerialization) As IList(Of JsonProperty)
        Dim props = type.GetProperties(BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance).
                            Select(Function(p) MyBase.CreateProperty(p, memberSerialization)).
                            Union(type.GetFields(BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance).
                            Select(Function(f) MyBase.CreateProperty(f, memberSerialization))).ToList()

        props.ForEach(Sub(p)
                          p.Writable = True
                          p.Readable = True
                      End Sub)
        Return props
    End Function
End Class

大多数代码转换器在VB.NET中将C#Lambda更改为Function(),但如果它们不需要返回类型,则应将它们转换为Sub()。这可能是导致您出现问题的原因。

答案 1 :(得分:2)

使用DataContractSerializer,您可以使用适当的属性标记要序列化的内容。您尝试使用它时没有分享您尝试过的内容或失败的方式,但具体如下:

Imports System.Runtime.Serialization
Imports System.ComponentModel

<DataContract>
Public Class Employee
    <DataMember(Name:="Name")>
    Public Property Name As String

    <DataMember(Name:="Dept")>
    Public Property Dept As String

    <DataMember(Name:="Index")>
    Public Property Index As Integer

    <DataMember(Name:="secret")>
    Private secret As Integer

    ' most serializers require a simple ctor
    Public Sub New()

    End Sub

    Public Sub SetValue(n As Integer)
        secret = n
    End Sub

End Class

在课程上使用<DataContract>,在成员上使用<Datamember>。然后序列化:

' test data
Dim emp As New Employee With {.Name = "Ziggy", .Dept = "Manager", .Index = 2}
emp.SetValue(4)            ' set private member value

Dim ser = New DataContractSerializer(GetType(Employee))

Using fs As New FileStream("C:\Temp\DCTest.xml", FileMode.OpenOrCreate),
                      xw = XmlWriter.Create(fs)
    ser.WriteObject(xw, emp)
End Using

输出:

<?xml version="1.0" encoding="utf-8"?>
   <Employee xmlns:i="..." xmlns="...">
      <Dept>Manager</Dept>
      <Index>2</Index>
      <Name>Ziggy</Name>
      <secret>4</secret>
 </Employee>

对于此类事物的一刀切的功能,我个人倾向于将序列化视为类或应用程序或任务特定(我也赞成通过XML 100到1的二进制序列化) ),但是......

Private Function SerializeToString(Of T)(obj As T) As String
    ' there might be more efficient ways...
    ' this is off the top of my head
    Dim ser = New DataContractSerializer(GetType(T))
    Dim xml As String

    Using ms As New MemoryStream()
        ser.WriteObject(ms, obj)
        ms.Position = 0
        Using sr As New StreamReader(ms)
            xml = sr.ReadToEnd
        End Using
    End Using

    Return xml
End Function

Public Function DeserializeFromString(Of T)(xmlString As String) As T

    Dim ser = New DataContractSerializer(GetType(T))
    Dim ret As T

    Using sr As New StringReader(xmlString),
        ms As New MemoryStream(Encoding.Default.GetBytes(sr.ReadToEnd))
        ret = CType(ser.ReadObject(ms), T)
    End Using

    Return ret

End Function

似乎在往返测试中工作:

Dim emp As New Employee With {.Name = "Ziggy", .Dept = "Manager", .Index = 2}
emp.SetValue(4)

Dim str = SerializeToString(Of Employee)(emp)

Dim emp2 As Employee = DeserializeFromString(Of Employee)(str)

' serialize to disk (not shown)
If SerializeToFile("C:\Temp\DCTest.xml", emp) Then

    Dim emp3 As Employee = DeserializeFromFile(Of Employee)("C:\Temp\DCTest.xml")

End If

反序列化版本具有正确的私有秘密值:

enter image description here

答案 2 :(得分:1)

我很久以前就试过了,发现最好的解决方案是使用.NET内置二进制序列化,它在保留内部类变量方面最有效。 XML序列化等策略经常因我编写的复杂类而失败,并且在向类添加新成员时,我经常不得不更新我为实现IXmlSerializable而编写的方法。

上下文是我需要将代码从网站传递到Web服务 - 这是我可以将数据从Web区域通过防火墙传递到内部业务服务器的唯一允许方式。我希望将整个对象数据传递到Web服务中以运行一些业务逻辑并执行持久化到数据库位。

解决方案是将二进制序列化为字节数组 - 这是Web服务的允许输入类型 - 以及Web服务中的二进制反序列化。

如果序列化的示例,给定一个返回布鲁尔结果的Web服务并假设您要序列化的对象称为myObject

    dim result as boolean
    Dim myStream As New System.IO.MemoryStream()
    Dim myFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
    myFormatter.Serialize(myStream, myObject)  '1. Serialise into stream

    Dim bytes As Byte() = New Byte(myStream.Length) {}
    myStream.Position = 0
    myStream.Read(bytes, 0, myStream.Length)  '2. read stream into byte array

    result = myWebService.PersistObject(bytes) 

并且这里是另一端的反序列化位,假设对象的类被称为小部件,并且数据作为名为objectBytes的变量传递给方法

    Dim myWidget As New Widget
    Dim myStream As New System.IO.MemoryStream()
    Dim myFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
    myStream.Write(objectBytes, 0, objectBytes.Length)
    myStream.Position = 0
    myWidget = myFormatter.Deserialize(myStream)

请注意,为此,您必须在接收端包含定义类的类库。但很酷的是它适用于任何类