I am using an XmlSerializer to DeepCopy an object, but somehow it's not copying the nested properties.
<TestClass()>
Public Class ObjectClonerTest
ReadOnly _target As IObjectCloner = New ObjectCloner()
<TestMethod()>
Public Sub DeepCopy_ComplexSourceObject_NestedObjectCopied()
Dim source As New SerialisableComplexGenericParameterHelper()
source.Data() = 1
source.ComplexValue.Data() = 2
Dim actual = _target.DeepCopy(source)
Assert.AreEqual(1, actual.Data())
Assert.AreEqual(2, actual.ComplexValue.Data())
End Sub
Public Class SerialisableComplexGenericParameterHelper
Public Property Data() As Integer
Public ReadOnly ComplexValue As New SerialisableGenericParameterHelper()
End Class
Public Class SerialisableGenericParameterHelper
Public Property Data() As Integer
End Class
End Class
The copy function:
Public Function DeepCopy(Of T)(ByVal pSource As T) As T Implements IObjectCloner.DeepCopy
Using memoryStream As New MemoryStream()
Dim binaryFormatter As New XmlSerializer(GetType(T))
binaryFormatter.Serialize(memoryStream, pSource)
memoryStream.Position() = 0
Return DirectCast(binaryFormatter.Deserialize(memoryStream), T)
End Using
End Function
XmlSerializer does not serialize ReadOnly fields. Removing ReadOnly fixed the issue.
ComplexValue is declared as:
Public ReadOnly ComplexValue As New SerialisableGenericParameterHelper()
This isn't a property - its only a field. Fixing this should sort it I think.
Related
I have a class Person which I can serialize with the following code, but I can’t figure out how to deserialize the file back to the class.
I would be grateful for help on this. Thank you.
Imports Newtonsoft.Json
Imports Windows.Storage
Imports Windows.Storage.Streams
Public Class Person
Public Property Name As String
Public Property Age As Integer
Public Property Gender As String
End Class
Public NotInheritable Class MainPage
Inherits Page
Private p As Person
Private pList As New List(Of Person)
Private Async Sub Save()
Dim jsonContents As String = JsonConvert.SerializeObject(pList)
Dim localFolder As StorageFolder = ApplicationData.Current.LocalFolder
Dim textFile As StorageFile = Await localFolder.CreateFileAsync("a.txt", CreationCollisionOption.ReplaceExisting)
Using textStream As IRandomAccessStream = Await textFile.OpenAsync(FileAccessMode.ReadWrite)
Using textWriter As New DataWriter(textStream)
textWriter.WriteString(jsonContents)
Await textWriter.StoreAsync()
End Using
End Using
End Sub
End Class
I tried the following but it doesn’t work.
Private Async Sub GetData()
Dim localFolder As StorageFolder = ApplicationData.Current.LocalFolder
Dim textFile = Await localFolder.GetFileAsync("a.txt")
Dim readFile = Await FileIO.ReadTextAsync(textFile)
Dim obj As RootObject = JsonConvert.DeserializeObject(Of RootObject)(readFile)
End Sub
Public Class RootObject
'Public Property pList1() As List(Of Person)
Public Property Name() As String
Public Property Age() As Integer
Public Property Gender() As String
End Class
You should make sure your VB class object's property in accordance with the JSON key or JSON name.
For example using your sample JSON data in your comment:
Since your JSON data is not complete, I modify it as the following:
{"pList1":[{"Name":"Henrik","Age":54,"Gender":"Mand"},{"Name":"Lone","Age":50,"Gender":"Kvinde"},{"Name":"Niels","Age":24,"Gender":"Mand"},{"Name":"Pernille","Age":26,"Gender":"Kvinde"}]}
You can keep the above Json data in a file named my.txt, if you want to deserialize the Json data to VB object, your VB objects classes should be as the following two classes:
Public Class Person
Public Property Name As String
Public Property Age As Integer
Public Property Gender As String
End Class
Public Class RootObject
Public Property pList1() As List(Of Person)
End Class
Please pay attention to that: the pList1 property of RootObject class is corresponding to the pList1 key or name in the JSON data.
Then you should be able to use the JsonConvert class to deserialize to RootObject.
Private Async Sub GetData()
Dim localFolder As StorageFolder = ApplicationData.Current.LocalFolder
Dim textFile = Await localFolder.GetFileAsync("my.txt")
Dim readFile = Await FileIO.ReadTextAsync(textFile)
Dim obj As RootObject = JsonConvert.DeserializeObject(Of RootObject)(readFile)
End Sub
I create a class based on other class. I'm try to assign the class a value in its constructor, but get an error.
Error 1 'Me' cannot be the target of an assignment.
is there a way to solve it..? Here is a code I've created
Public Class tabelDSN
Inherits DataTable
Public Sub New(ByVal dt As DataTable)
Me = dt.Copy()
End Sub
Default Public ReadOnly Property rw(ByVal idx) As DataRow
Get
Return Me.Rows(idx)
End Get
End Property
End Class
You could make your tabelDSN class be a wrapper for the DataTable class, by removing the Inherits and adding a private DataTable field to hold the constructor parameter. The code would look like this:
Public Class tabelDSN
Private _dt as DataTable
Public Sub New(ByVal dt As DataTable)
_dt = dt.Copy()
End Sub
Default Public ReadOnly Property rw(ByVal idx) As DataRow
Get
Return _dt.Rows(idx)
End Get
End Property
End Class
I'm trying to make a game using vb,so i made this class:
Imports Microsoft.VisualBasic.PowerPacks
Module EntityI
Public entities As List(Of Entity)
Public Function getEntity(ByVal uuid As Guid) As Entity
For i = 0 To entities.Count - 1
If entities.Item(i).getUUID = uuid Then
Return entities.Item(i)
End If
Next
Return Nothing
End Function
End Module
Public Class Entity
Private uuid As Guid
Private location As Location
Private shape As OvalShape
Public Sub New()
uuid = System.Guid.NewGuid
Dim canvas As New PowerPacks.ShapeContainer
canvas.Parent = Game
shape = New OvalShape With {.Parent = canvas}
shape.SetBounds(50, 50, 50, 50)
save()
End Sub
Public Function getUUID() As Guid
Return uuid
End Function
Public Function getLocation() As Location
Return location
End Function
Public Sub teleport(ByVal location As Location)
Me.location = location
End Sub
Private Sub save()
entities.add(Me)
End Sub
End Class
So this gives an error at entities.add(Me) (NullRefernceException).
Is it something with the list or something else?
Help please.
You need to have
Public entities As New List(Of Entity)
You are new list. You are only declaring the variable as a list. Change Public entities As List(Of Entity) to Public entities As New List(Of Entity)
I'm using Entity Framework 6 , with Database First. The model is created using wizard from existing Sql server database.
I'm using this code to do a deep clone :
Imports System.ComponentModel
Imports System.Collections
Imports System.Data.Entity.Core.Objects.DataClasses
Imports System.Runtime.Serialization
Imports System.IO
Imports System.Reflection
Imports System.Runtime.CompilerServices
Module Extensions
Private Function ClearEntityObject(Of T As Class)(ByVal source As T, ByVal bCheckHierarchy As Boolean) As T
If (source Is Nothing) Then
Throw New Exception("Null Object cannot be cloned")
End If
Dim tObj As Type = source.GetType
If (Not tObj.GetProperty("EntityKey") Is Nothing) Then
tObj.GetProperty("EntityKey").SetValue(source, Nothing, Nothing)
End If
If bCheckHierarchy Then
Dim PropertyList As List(Of PropertyInfo) = Enumerable.ToList(Of PropertyInfo)((From a In source.GetType.GetProperties
Where a.PropertyType.Name.Equals("ENTITYCOLLECTION`1", StringComparison.OrdinalIgnoreCase)
Select a))
Dim prop As PropertyInfo
For Each prop In PropertyList
Dim keys As IEnumerable = DirectCast(tObj.GetProperty(prop.Name).GetValue(source, Nothing), IEnumerable)
Dim key As Object
For Each key In keys
Dim childProp As EntityReference = Enumerable.SingleOrDefault(Of PropertyInfo)((From a In key.GetType.GetProperties
Where (a.PropertyType.Name.Equals("EntityReference`1", StringComparison.OrdinalIgnoreCase))
Select a)).GetValue(key, Nothing)
ClearEntityObject(childProp, False)
ClearEntityObject(key, True)
Next
Next
End If
Return source
End Function
<Extension()> _
Public Function ClearEntityReference(ByVal source As Object, ByVal bCheckHierarchy As Boolean) As Object
Return ClearEntityObject(source, bCheckHierarchy)
End Function
<Extension()> _
Public Function Clone(Of T)(ByVal source As T) As T
Dim ser As New DataContractSerializer(GetType(T))
Using stream As MemoryStream = New MemoryStream
ser.WriteObject(stream, source)
stream.Seek(0, SeekOrigin.Begin)
Return DirectCast(ser.ReadObject(stream), T)
End Using
End Function
End module
Now , I try to use this code like this :
Private Sub DoClone
Dim litm, newitm As MyObject
litm = context.MyObjects.FirstOrDefault
newitm = litm.Clone()
newitm.ClearEntityReference(True)
context.MyObjects.Add(newitm)
context.SaveChanges()
End Sub
I get an error :
An unhandled exception of type
'System.Runtime.Serialization.SerializationException' occurred in
System.Runtime.Serialization.dll
Additional information:Type
'System.Data.Entity.DynamicProxies.MyObject_F2FFE64DA472EB2B2BDF7E143DE887D3845AD9D1731FD3107937062AC0C2E4BB'
with data contract name
'MyObject_F2FFE64DA472EB2B2BDF7E143DE887D3845AD9D1731FD3107937062AC0C2E4BB:http://schemas.datacontract.org/2004/07/System.Data.Entity.DynamicProxies'
is not expected.
Consider using a DataContractResolver or add any
types not known statically to the list of known types - for example,
by using the KnownTypeAttribute attribute or by adding them to the
list of known types passed to DataContractSerializer.
This is my model that I use :
Partial Public Class Myobject
Public Property id As Integer
Public property name as string
Public Overridable Property chld As ICollection(Of chld) = New HashSet(Of chld)
Public Overridable Property chld1 As ICollection(Of chld1) = New HashSet(Of chld1)
End Class
Partial Public Class chld
Public Property id As Integer
Public Property date1 as DateTime
Public Property quantity as Integer
Public Property ParentID as integer
Public Overridable Property MyObj1 As MyObject
End Class
Partial Public Class chld1
Public Property id As Integer
Public Property nm as string
Public Property ParentID as integer
Public Overridable Property MyObj1 As MyObject
End Class
I posted a similar question before, which worked in C# (thanks to the community), but the actual problem was in VB.Net ( with option strict on). Problem is that tests are not passing.
Public Interface IEntity
Property Id() As Integer
End Interface
Public Class Container
Implements IEntity
Private _name As String
Private _id As Integer
Public Property Id() As Integer Implements IEntity.Id
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
Public Property Name() As String
Get
Return _name
End Get
Set(ByVal value As String)
_name = value
End Set
End Property
End Class
Public Class Command
Public Sub ApplyCommand(ByRef entity As IEntity)
Dim innerEntity As New Container With {.Name = "CommandContainer", .Id = 20}
entity = innerEntity
End Sub
End Class
<TestFixture()> _
Public Class DirectCastTest
<Test()> _
Public Sub Loosing_Value_On_DirectCast()
Dim entity As New Container With {.Name = "Container", .Id = 0}
Dim cmd As New Command
cmd.ApplyCommand(DirectCast(entity, IEntity))
Assert.AreEqual(entity.Id, 20)
Assert.AreEqual(entity.Name, "CommandContainer")
End Sub
End Class
The same is true in VB as in C#. By using the DirectCast, you're effectively creating a temporary local variable, which is then being passed by reference. That's an entirely separate local variable from the entity local variable.
This should work:
Public Sub Losing_Value_On_DirectCast()
Dim entity As New Container With {.Name = "Container", .Id = 0}
Dim cmd As New Command
Dim tmp As IEntity = entity
cmd.ApplyCommand(tmp)
entity = DirectCast(tmp, Container)
Assert.AreEqual(entity.Id, 20)
Assert.AreEqual(entity.Name, "CommandContainer")
End Sub
Of course it would be simpler just to make the function return the new entity as its return value...