VB.Net Serializing and Deserializing Private Members - Simple Example
I need to serialize any object (to xml, json, other - not really caring) for the purpose of passing / cloning, etc.
I found the newtonsoft.json library to be pretty easy to use for this, except that it doesn't serialize private members. I found examples in C # that seem to offer what I need using contracts, but I can't seem to get them to work.
I would appreciate a simple example:
eg.
Class Person
Private _NI as integer
Public Name as string
End Class
I need two functions:
Function SerializePerson(P as person) as string
Function DeSerializePerson(SerializedText as string) as Person
Ideally, I want an approach that is easy to duplicate for any object.
source to share
The trick with Newtonsoft is to modify JsonSerializerSettings
and pass them to JsonConvert.SerializeObject
. This answer has full details of this change: fooobar.com/questions/149807 / ...
Edit
I didn't think the associated code was in C # since I work so often. My apologies. I have translated the code below:
Dim settings As New JsonSerializerSettings() With {.ContractResolver = New MyContractResolver()}
Dim json As String = JsonConvert.SerializeObject(obj, settings)
And the contract resolver:
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
Most code converters change C # Lambda to Function()
in VB.NET, but if they don't need a return type they should be converted to Sub()
. This was probably the cause of your problem.
source to share
By using DataContractSerializer you put the things you want to serialize with the appropriate attribute. You didn't share what you tried or how it failed when you tried to use it, but here's how:
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
Use <DataContract>
for class and <Datamember>
for members. Then serialize:
' 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
Output:
<?xml version="1.0" encoding="utf-8"?> <Employee xmlns:i="..." xmlns="..."> <Dept>Manager</Dept> <Index>2</Index> <Name>Ziggy</Name> <secret>4</secret> </Employee>
As far as functions of the same size for all types of this type are concerned, I personally tend to view serialization as a class or application or specific task (I also prefer binary serialization over XML 100 to 1), but ...
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
It seems that we are working in a "round" test:
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
The deserialized version has the correct private secret:
source to share
I tried this a long time ago and found that the best solution was to use .NET inline binary serialization. This was most efficient when preserving internal class variables. Tactics like XML serializaiton often failed with complex classes I wrote, and when adding new members to the class, I often had to update the methods I wrote to implement IXmlSerializable.
The context was that I needed to pass code from the website to the web service - this was the only allowed way to pass data from the web zone through our firewall to our internal business servers. I wanted to pass all the object data to a web service to run some business logic and focus on the database bit.
The solution was binary serialization to a byte array - a valid input type for a web service - and binary deserialization in a web service.
An example of how to serialize, given a web service that returns a boolean result for sucess and assuming the object you want to serialize is called 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)
and here's a deserialization bit on the other end, assuming the object class was named widget and the data was passed to the methods as a variable called 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)
Note that this requires the class libraries to be included that define the class on the host. But it's great that it works for any class
source to share