Serialize Keyedcollection which contains another keyedcollection - vb.net

I recently started working with vb.net coming from vba.
For my planned application I wanted to create a KeyedCollection which stores another keyedcollection. The reason is I have a kind of database, where I want to be able to store for a varying number of "Parametersets", an now undefined number of "List_of_Parameters" where arrays of coefficients are stored.
My problem lies in serialization.
When I run the XMLSerialization only the deepestly nested elements are stored correctly. The elements one level above are just called "Array_of_node" and all the variables ignored except the keyedcollection.
I had expected that instead of I would see the classname. Furthermore, I had expected to see something like this.
<Database>
<Species>
<Name>Parameterset 1</Name>
<Node>...</Node>
<Node>...</Node>
</Species>
...
Any help would be really appreciated,
Best regards,
Johannes.
This is the xml-output I get:
<?xml version="1.0" encoding="utf-8"?>
<Database xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ArrayOfNode>
<Node>
<Name>ListOfParameters_1</Name>
<value>
<double>1</double>
<double>2</double>
<double>3</double>
</value>
</Node>
<Node>
<Name>ListOfParameters_2</Name>
<value>
<double>5</double>
<double>6</double>
</value>
</Node>
</ArrayOfNode>
<ArrayOfNode>
<Node>
<Name>ListOfParameters_1</Name>
<value>
<double>7</double>
<double>8</double>
<double>9</double>
</value>
</Node>
<Node>
<Name>ListOfParameters_2</Name>
<value>
<double>10</double>
<double>11</double>
</value>
</Node>
</ArrayOfNode>
</Database>
This is my Database:
Imports System.Collections.ObjectModel
Imports TestListofList
Imports System.Xml.Serialization
Imports System.IO
<Serializable>
<XmlRootAttribute("Database")>
Public Class Database
Inherits KeyedCollection(Of String, Species)
Private myName As String
Public Property Name() As String
Get
Return myName
End Get
Set(ByVal value As String)
myName = value
End Set
End Property
Protected Overrides Function GetKeyForItem(item As Species) As String
Return item.Name
End Function
Sub New()
MyBase.New()
End Sub
Sub New(ByVal name As String)
Me.New()
myName = name
End Sub
Public Sub SerializeToXML(ByVal filename As String)
Dim locXMLWriter As New XmlSerializer(GetType(Database))
Dim locXMLFile As New StreamWriter(filename)
locXMLWriter.Serialize(locXMLFile, Me)
locXMLFile.Flush()
locXMLFile.Close()
End Sub
End Class
This is the class which stores the List of "List_of_Coeffiencts" for the different parametersets:
Imports System.Collections.ObjectModel
Imports TestListofList
Imports System.Xml.Serialization
<Serializable>
Public Class Species
Inherits KeyedCollection(Of String, Node)
Public myName As String
Public Property Name() As String
Get
Return myName
End Get
Set(ByVal value As String)
myName = value
End Set
End Property
Protected Overrides Function GetKeyForItem(item As Node) As String
Return item.Name
End Function
Sub New()
MyBase.New()
End Sub
Sub New(ByVal Name As String)
Me.New()
myName = Name
End Sub
End Class
And this is the final "List of coefficients"
Public Class Node
Private myName As String
Private myvalue As Double()
Public Property Name() As String
Get
Return myName
End Get
Set(ByVal value As String)
myName = value
End Set
End Property
Public Property value() As Double()
Get
Return myvalue
End Get
Set(ByVal value As Double())
myvalue = value
End Set
End Property
Sub New()
End Sub
Sub New(ByVal Name As String, value() As Double)
myName = Name
myvalue = value
End Sub
End Class
And this my sample main program:
Module Module1
Sub Main()
Dim dot As Node
Dim molecule As Species
Dim data As New Database
molecule = New Species("Parameterset1")
data.Add(molecule)
dot = New Node("ListOfParameters_1", New Double() {1, 2, 3})
data.Item("Parameterset1").Add(dot)
dot = New Node("ListOfParameters_2", New Double() {5, 6})
data.Item("Parameterset1").Add(dot)
molecule = New Species("Parameterset2")
data.Add(molecule)
dot = New Node("ListOfParameters_1", New Double() {7, 8, 9})
data.Item("Parameterset2").Add(dot)
dot = New Node("ListOfParameters_2", New Double() {10, 11})
data.Item("Parameterset2").Add(dot)
data.SerializeToXML("C:\test.xml")
End Sub
End Module

Try this.... (New code and classes)
Imports System.IO
Imports System.Xml.Serialization
Module Module1
Sub Main()
Dim Database1 As New List(Of Species)
Dim Species1 As New Species
Species1.Name = "SpeciesName1"
Dim Parameterset1 As New Parameterset
Parameterset1.Name = "Parameterset1"
Parameterset1.Node.Add("1")
Parameterset1.Node.Add("2")
Parameterset1.Node.Add("3")
Species1.Parameterset.Add(Parameterset1)
Database1.Add(Species1)
Dim Species2 As New Species
Species2.Name = "SpeciesName2"
Dim Parameterset2 As New Parameterset
Parameterset2.Name = "Parameterset1"
Parameterset2.Node.Add("1")
Parameterset2.Node.Add("2")
Species2.Parameterset.Add(Parameterset2)
Database1.Add(Species2)
' to Serialize the object to test.xml
Serialize(Database1)
' and to Deserialize from test.xml
Dim Database2 As New List(Of Species)(Deserialize())
End Sub
Private Sub Serialize(SpeciesList As List(Of Species))
' Use a file stream here.
Using fs As New StreamWriter("test.xml")
' Construct a XmlSerializer and use it
' to serialize the data to the stream.
Dim SerializerObj As New XmlSerializer(GetType(List(Of Species)))
Try
' Serialize EmployeeList to the file stream
SerializerObj.Serialize(fs, SpeciesList)
Catch ex As Exception
Console.WriteLine(String.Format("Failed to serialize. Reason: {0}", ex.Message))
End Try
End Using
End Sub
Private Function Deserialize() As List(Of Species)
Dim EmployeeList2 = New List(Of Species)()
' Create a new file stream for reading the XML file
Using fs = New StreamReader("test.xml")
' Construct a XmlSerializer and use it
' to serialize the data from the stream.
Dim SerializerObj = New XmlSerializer(GetType(List(Of Species)))
Try
' Deserialize the hashtable from the file
EmployeeList2 = DirectCast(SerializerObj.Deserialize(fs), List(Of Species))
Catch ex As Exception
Console.WriteLine(String.Format("Failed to serialize. Reason: {0}", ex.Message))
End Try
End Using
' return the Deserialized data.
Return EmployeeList2
End Function
End Module
<XmlRoot(ElementName:="Parameterset")>
Public Class Parameterset
<XmlElement(ElementName:="Name")>
Public Property Name As String
<XmlElement(ElementName:="Node")>
Public Property Node As List(Of String) = New List(Of String)
End Class
<XmlRoot(ElementName:="Species")>
Public Class Species
<XmlElement(ElementName:="Name")>
Public Property Name As String
<XmlElement(ElementName:="Parameterset")>
Public Property Parameterset As List(Of Parameterset) = New List(Of Parameterset)
End Class
<XmlRoot(ElementName:="Database")>
Public Class Database
<XmlElement(ElementName:="Species")>
Public Property Species As List(Of Species) = New List(Of Species)
End Class
The XML should look like this now.....
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfSpecies xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Species>
<Name>SpeciesName1</Name>
<Parameterset>
<Name>Parameterset1</Name>
<Node>1</Node>
<Node>2</Node>
<Node>3</Node>
</Parameterset>
</Species>
<Species>
<Name>SpeciesName2</Name>
<Parameterset>
<Name>Parameterset1</Name>
<Node>1</Node>
<Node>2</Node>
</Parameterset>
</Species>
</ArrayOfSpecies>

Related

store setting in relative path to executable aplication

I am developing an application to run from USB, is it like a dock/launcher application but I have a problem.
I want to use My.Settings class to save my app settings, but it saves the setting file in AppData folder e.g. C:\Users\<user_name>\AppData\Local\...\...\user.config
I don't want that. I want to save in a path and name of my defined, e.g. My.Application.Info.DirectoryPath & "\Settings.xml"
How can I achieve this?
Update Example of final XML:
<?xml version="1.0" encoding="utf-8"?>
<conf>
<pos>1</pos>
<btn index="1" value="D:\League of Legends\" perm="true">LeagueClient.exe</btn>
<btn index="2" value="D:\RuneLite\" perm="false">RuneLite.exe</btn>
<btn index="3" value="" perm="false"></btn>
<btn index="4" value="" perm="false"></btn>
</conf>
Full project in Github coming soon!!!
Work way for me :
Imports System.IO
Imports System.Xml
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
ValConfFile()
End Sub
Public Sub ValConfFile()
If File.Exists("config.xml") = False Then : CreateConfXML() : End If
End Sub
Public Sub CreateConfXML()
Dim obj As Object
Dim archivo As Object
Dim x As Integer = 1
obj = CreateObject("Scripting.FileSystemObject")
archivo = obj.CreateTextFile("config.xml", True)
archivo.WriteLine("<?xml version='1.0' encoding='utf-8'?>")
archivo.WriteLine("<conf>")
archivo.WriteLine("<pos>1</pos>")
For x = 1 To 4
archivo.WriteLine("<btn index='" & CStr(x) & "' value='' perm='false'></btn>")
Next
archivo.WriteLine("</conf>")
archivo.Close()
End Sub
End Class
XML Serialization can be used for this, and is actually fairly straightforward. All you need to do is design one or more classes for your data, then apply the appropriate attributes (their name are in the form Xml...Attribute) in order to serialize them the way you want.
In this setup I've used four different attributes:
XmlElement - Usually the most common one. Specifies that a property will be serialized as an element of its own. The resulting name can be customized by setting the ElementName parameter in the constructor.
If used on lists or arrays, it applies to each item in the collection.
XmlRoot - Pretty much the same as XmlElement, but used for the root element (the class itself).
XmlAttribute - Specifies that a property will be serialized as an attribute (name="value") applied to the parent object, instead of as an element inside it.
XmlText - Specifies that a property's value will be serialized as the contents between the tags of the parent object (i.e. <object>property value</object>).
Imports System.IO
Imports System.Xml
Imports System.Xml.Serialization
<XmlRoot(ElementName:="conf")>
Public Class Config
<XmlElement(ElementName:="pos")>
Public Property Position As Integer
<XmlElement(ElementName:="btn")>
Public Property Buttons As New List(Of ConfigButton)
Public Sub New()
End Sub
Public Sub New(ByVal Position As Integer)
Me.Position = Position
End Sub
Public Shared Function Load(ByVal File As String) As Config
Using FStream As New FileStream(File, FileMode.Open, FileAccess.Read, FileShare.Read)
Dim Serializer As New XmlSerializer(GetType(Config))
Return Serializer.Deserialize(FStream)
End Using
End Function
Public Sub Save(ByVal File As String)
Using FStream As New FileStream(File, FileMode.Create, FileAccess.Write, FileShare.None)
Dim Serializer As New XmlSerializer(GetType(Config))
Serializer.Serialize(FStream)
End Using
End Sub
End Class
Public Class ConfigButton
<XmlText()>
Public Property DisplayName As String
<XmlAttribute("index")>
Public Property Index As Integer
<XmlAttribute("perm")>
Public Property Perm As Boolean
<XmlAttribute("value")>
Public Property Value As String
Public Sub New()
End Sub
Public Sub New(ByVal DisplayName As String, ByVal Value As String, ByVal Index As Integer, ByVal Perm As Boolean)
Me.DisplayName = DisplayName
Me.Value = Value
Me.Index = Index
Me.Perm = Perm
End Sub
End Class
Usage example:
Private cfg As Config
'
'Loading the config.
'
Private Sub Form1_Load(sender As Object, e As EventArgs) Handled MyBase.Load
If File.Exists("config.xml") Then
cfg = Config.Load("config.xml")
Else
cfg = New Config()
End If
End Sub
'
'Saving the config.
'
Private Sub Form1_FormClosed(sender As Object, e As EventArgs) Handles Me.FormClosed
cfg.Save("config.xml")
End Sub
Adding a button:
cfg.Buttons.Add(New ConfigButton("RuneLite.exe", "D:\RuneLite\", 2, False))
Iterating buttons:
For Each btn As ConfigButton In cfg.Buttons
MessageBox.Show(btn.DisplayName)
Next
Removing a button at a specific index:
'Removes the fourth button.
cfg.Buttons.RemoveAt(3)

Getting a property from the instantiator class

Not an experienced programmer, so probably not a hard question.
Developing a small application in VB.net in WPF.
I made 3 classes, EngineeringObject<==Inherits==PartOfInstallation<==Inherits==SensorActor
In the class SensorActor I'm trying to get a property of PartOfInstallation with the function MyBase.Name. But this goes directly to EngineeringObject. How do I solve this?
Public Class EngineeringObject
''Private declarations, alleen objecten die erven kunnen hieraan, of dmv van getters en setters
'Name of part
Private sName As String = "Naam"
'81346 Id's
Private sSystemId As String = "Functie" 'VentilationSystem, Pumpsystem
Private sLocationId As String = "Locatie" 'Room 0.0
Private sObjectId As String = "Object" 'Fan, Pump
'General
Private sPartNumber As String
Private sLinkToDatasheet As String
'Property's
Public Property Name() As String
Get
Return sName
End Get
Set(ByVal value As String)
sName = value
End Set
End Property
Public Property SystemId() As String
Get
Return sSystemId
End Get
Set(ByVal value As String)
sSystemId = value
End Set
End Property
Public Property PartNumber() As String
Get
Return sPartNumber
End Get
Set(ByVal value As String)
sPartNumber = value
End Set
End Property
Public Property LinkToDatasheet() As String
Get
Return sLinkToDatasheet
End Get
Set(ByVal value As String)
sLinkToDatasheet = value
End Set
End Property
Public Sub New()
End Sub
End Class
Public Class PartOfInstallation
Inherits EngineeringObject
'src: https://stackoverflow.com/questions/21308881/parent-creating-child-object
'src: https://stackoverflow.com/questions/16244548/how-to-create-a-list-of-parent-objects-where-each-parent-can-have-a-list-of-chil
Private lSensorActor As New List(Of SensorActor)
Public Function GetSensorActor()
Return Me.lSensorActor
End Function
Public Sub CreateSensorActor()
lSensorActor.Add(New SensorActor)
End Sub
End Class
Public Class SensorActor
Inherits PartOfInstallation
Dim sMyPartOfInstallation As String
Public Property MyPartOfInstallation As String
Get
Return sMyPartOfInstallation
End Get
Set(value As String)
sMyPartOfInstallation = MyBase.Name
End Set
End Property
End Class
If I understand it correctly, based on your comments, you want every SensorActor instantiated within a PartOfInstallation instance to get the same name as that instance.
If so, then just add a second constructor to your SensorActor class allowing you to pass a name for it as well:
Public Class SensorActor
Inherits PartOfInstallation
...your code...
Public Sub New() 'Empty constructor, for if/when you don't want to set the name immediately.
End Sub
Public Sub New(ByVal Name As String)
Me.Name = Name
End Sub
End Class
Now in your PartOfInstallation class you can do:
Public Sub CreateSensorActor()
lSensorActor.Add(New SensorActor(Me.Name)) 'Here, "Me" refers to the current PartOfInstallation instance.
End Sub
Alternatively you can make the SensorActor constructor take a PartOfInstallation instance instead, allowing you to copy any properties you like:
Public Class SensorActor
Inherits PartOfInstallation
...your code...
Public Sub New()
End Sub
Public Sub New(ByVal BasedOnPOI As PartOfInstallation)
Me.Name = BasedOnPOI.Name
End Sub
End Class
Thus making the code in the PartOfInstallation class:
Public Sub CreateSensorActor()
lSensorActor.Add(New SensorActor(Me))
End Sub
Read more about constructors: Object Lifetime: How Objects Are Created and Destroyed (Visual Basic) | Microsoft Docs
The result below, if there's room for improvement... always welcome.
SensorActor
Public Class SensorActor
Inherits PartOfInstallation
Dim sTemp As String
Public Overloads Property SystemId() As String
Get
Return Me.sSystemId
End Get
Set(ByVal value As String)
Me.sSystemId = sTemp + "." + value
End Set
End Property
Public Sub New(ByVal BasedOnPOI As PartOfInstallation)
sTemp = BasedOnPOI.SystemId
End Sub
End Class
PartOfInstallation
Public Class PartOfInstallation
Inherits EngineeringObject
'src: https://stackoverflow.com/questions/21308881/parent-creating-child-object
'src: https://stackoverflow.com/questions/16244548/how-to-create-a-list-of-parent-objects-where-each-parent-can-have-a-list-of-chil
Private lSensorActor As New List(Of SensorActor)
Public Function GetSensorActor()
Return Me.lSensorActor
End Function
Public Sub CreateSensorActor()
lSensorActor.Add(New SensorActor(Me))
End Sub
End Class
EngineeringObject
Public Class EngineeringObject
''Private declarations, alleen objecten die erven kunnen hieraan, of dmv van getters en setters
'Name of part
Private sName As String = "Naam"
'81346 Id's
Friend sSystemId As String = "Functie" 'VentilationSystem, Pumpsystem
Private sLocationId As String = "Locatie" 'Room 0.0
Private sObjectId As String = "Object" 'Fan, Pump
'General
Private sPartNumber As String
Private sLinkToDatasheet As String
'Property's
Public Property Name() As String
Get
Return sName
End Get
Set(ByVal value As String)
sName = value
End Set
End Property
Public Property SystemId() As String
Get
Return sSystemId
End Get
Set(ByVal value As String)
sSystemId = "=" + value
End Set
End Property
Public Property PartNumber() As String
Get
Return sPartNumber
End Get
Set(ByVal value As String)
sPartNumber = value
End Set
End Property
Public Property LinkToDatasheet() As String
Get
Return sLinkToDatasheet
End Get
Set(ByVal value As String)
sLinkToDatasheet = value
End Set
End Property
Public Sub New()
End Sub
End Class

(De)Serializing in VB.Net

I have written a class (containing only properties). The code is shortened, here is only the part of the code what I want to do:
Public Class test
Public Overrides Function ToString() As String
'Das Objekt lebensmittel serialisieren
Dim ser As New Xml.Serialization.XmlSerializer(Me.GetType)
Dim sw As New IO.StringWriter
ser.Serialize(sw, Me)
Return sw.tostring
End Function
Public Sub New()
End Sub
Public Sub New(ByVal t As String)
Dim deser As New Xml.Serialization.XmlSerializer(Me.GetType)
Dim ms As New IO.MemoryStream(System.Text.Encoding.Unicode.GetBytes(t))
Me = CType(deser.Deserialize(ms), test) 'This throws an error
End Sub
End Class
What I want to do is to overload the New() operator and deserialize the string to this class, like:
Dim x As New test(string)
How can I do this? The marked line throws an error in Sub New.

Returning object instead of string and integer

I'm working on a vb.net project where a user can save and open a binary text.
However, the problem is that the when opening a file, it returns a object instead of string and integer.
It returns Projectname.Classname
What I want it to return is name and age that the user entered when saving the file.
Here is my code:
''' <summary>
''' When the user clicks open
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub OpenToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles OpenToolStripMenuItem.Click
Try
If (openFileDialog1.ShowDialog = DialogResult.OK) Then
thefilename = openFileDialog1.FileName
Dim message = animalmgr.ReadFile(thefilename)
'Getting method from manager
If (Not (message) Is Nothing) Then
message.ToList().ForEach(Sub(msg) Resultlst.Items.Add(msg))
Else
UpdateResults()
End If
End If
Catch ex As Exception
MessageBox.Show(ex.ToString, "Error in opening file")
End Try
End Sub
ReadFile function in the manager class:
''' <summary>
''' Reads a file from binary
''' </summary>
''' <param name="filename"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function ReadFile(ByVal filename As String) As Animal()
Dim BinSerial As BinSerializerUtility = New BinSerializerUtility
Dim animals = BinSerial.BinaryFileDeSerialize(Of Animal)(filename)
Return animals.ToArray
End Function
I did the exact same project in C# which worked perfectly, I don't know why it doesn't work in vb.net however. Does anyone know the problem and how to solve it?
Update:
Class BinSerializerUtility
Public Class BinSerializerUtility
Public Sub BinaryFileSerialize(ByVal objs As List(Of Animal), ByVal filePath As String)
Dim fileStream As FileStream = Nothing
Try
fileStream = New FileStream(filePath, FileMode.Create)
Dim b As BinaryFormatter = New BinaryFormatter
For Each obj In objs
b.Serialize(fileStream, obj)
Next
Finally
If (Not (fileStream) Is Nothing) Then
fileStream.Close()
End If
End Try
End Sub
Public Function BinaryFileDeSerialize(Of T As {Class})(ByVal filePath As String) As List(Of T)
Dim list = New List(Of T)
If Not File.Exists(filePath) Then
Throw New FileNotFoundException(("The file" + " was not found. "), filePath)
End If
Dim fileStream = New FileStream(filePath, FileMode.Open)
Dim b As BinaryFormatter = New BinaryFormatter
While (fileStream.Position < fileStream.Length)
list.Add(CType(b.Deserialize(fileStream), T))
End While
Return list
End Function
End Class
Animal class:
<Serializable()>
Public MustInherit Class Animal
Implements IAnimal
Private theCatagorytype As Categorytype
Private m_name As String
Private m_age As Integer
Private m_gender As String
Private m_id As Integer
Public Sub New(ByVal typ As Categorytype)
MyBase.New()
theCatagorytype = typ
End Sub
Public Sub New()
MyBase.New()
End Sub
#Region "Properties"
Public Overridable ReadOnly Property Description As String
Get
Return String.Format("{0}, {1}, {2}, {3}", Me.Id, Me.Name, Me.Age, Me.Gender)
End Get
End Property
Public Property Categorytype As Categorytype
Get
Return theCatagorytype
End Get
Set(value As Categorytype)
theCatagorytype = value
End Set
End Property
Public Property Id As Integer Implements IAnimal.Id
Get
Return m_id
End Get
Set(value As Integer)
If (value > 0) Then
m_id = value
End If
End Set
End Property
Public Property Name As String Implements IAnimal.Name
Get
Return m_name
End Get
Set(value As String)
m_name = value
End Set
End Property
Public Property Age As Integer
Get
Return m_age
End Get
Set(value As Integer)
m_age = value
End Set
End Property
Public Property Gender As String Implements IAnimal.Gender
Get
Return m_gender
End Get
Set(value As String)
m_gender = value
End Set
End Property
#End Region
Public MustOverride Function GetEaterType() As EaterType Implements IAnimal.GetEaterType
Public MustOverride Function GetSpecies() As String Implements IAnimal.GetSpecies
End Class
Rather than writing a class wrapper for an operation, I think it makes more sense to put such Save and Load functions in the class itself:
' in Animal class:
Friend Shared Function LoadData(myFile As String) As List(Of Animal)
Dim newList As List(Of Animal)
If File.Exists(myFile) Then
Using fs As New FileStream(myFile, FileMode.Open)
Dim bf = New BinaryFormatter
newList = CType(bf.Deserialize(fs), List(Of Animal))
End Using
Else
'return empty list of there is no file
newList = New List(Of Animal)
End If
Return newList
End Function
This seems simpler than trying to read one object at a time and place it in a List. Organizationally, it makes for fewer utility classes if the Foos, Bars and Animals can all serialize and deserialize themselves. To load them:
animals = Animal.LoadData(FileName)
' put animals in a ListBox:
myfrm.ListBox1.Items.AddRange(animals.ToArray)
Even better than making a copy of the animals for the list box, is to use your List(Of Animal) as the DataSource. That way, the user sees the same data set as your code is using.
When adding objects to a listbox or dropdown list, by default it uses ToString() to give you a string representation of the object, and the class name is the default ToString() implementation.
If you override ToString() in your Animal class, it will display however you need it to.
EDIT This is assuming you really want your ReadFile method to return a collection of Animal objects, and you want to add the actual objects to the listbox instead of truly adding string representations to the listbox.

Serialize an object that contains a list of objects

I'v build a NotInheritable Serilizer that serilizes all my classes and list of classes with success.
Until I'v build a list of class that contains a list of classes.
I'm getting the runtime Exeption: There was an error generating the XML document. resulting in a perfectly empty XML :(
These are my classes to serilize:
<System.Serializable> _
<System.Xml.Serialization.XmlInclude(GetType(StatisticItem))> _
Public Class Statistic
Public StatItem As New list(Of StatisticItem)
'Bla bla bla
end class
<Serializable> _
Public Class StatisticItem
Private stStatPath As String = ""
Private eStatType As StatType = 0
Private iBatchNumber As Int32 = 0
end class
And the serializer:
Public NotInheritable Class XmlSerializer
Public Shared Sub Serialize(Of T)(ByVal obj As T, sConfigFilePath As String)
Dim XmlBuddy As New System.Xml.Serialization.XmlSerializer(GetType(T))
Dim MySettings As New System.Xml.XmlWriterSettings()
MySettings.Indent = True
MySettings.CloseOutput = True
Dim MyWriter As System.Xml.XmlWriter=System.Xml.XmlWriter.Create(sConfigFilePath,MySettings)
XmlBuddy.Serialize(MyWriter,obj)
MyWriter.Flush()
MyWriter.Close()
' ----- OLD CODE FOR SERIALIZE, NEXTLINE IN XML DOESNT WORK ON WIN CE -------,
' B.T.W. Using This code to serilize gives the exact same fault
'Dim XmlBuddy As New System.Xml.Serialization.XmlSerializer(GetType(T))
'Dim objStreamWriter As New StreamWriter(sConfigFilePath)
'XmlBuddy.Serialize(objStreamWriter, obj)
'objStreamWriter.Close()
End Sub
end class
And this is the call:
XmlSerializer.Serialize(Of list(Of Statistic))(StatCollection, CommCtrl.PathStatisticFile)
If i comment the list in StatisticItem everything works.
I think if I Implement IXmlSerializable in StatisticItem I can tell the serializer how to work to make it work, but I see other code example on the internet where this works without all this effort
and I prefer a clean solution, that is about the same as all my other classes.
Hope one of you guys can help me out
Yes solved!, To be honest I changed so much small things that I still don't know what the cause was.
Maybe that there were still some private members.
Anyway, maybe the code can be useful for anyone:
Public Class Statistic
'Properties
Private eStatName As String
Private eStatSort As StatSort
Private StatItem As New list(Of StatisticItem)
Public Property Name() As String
Get
Return eStatName
End Get
Set(ByVal value As String)
eStatName = value
End Set
End Property
'Other public properties
End class
Public Class StatisticItem
Private stStatPath As String = ""
Private eStatType As StatType = 0
Private iBatchNumber As Int32 = 0
Public Property Path() As String
Get
Return stStatPath
End Get
Set(ByVal Value As String)
stStatPath = Value
End Set
End Property
' The other Public Properties
Serializer:
Public NotInheritable Class XmlSerializer
''' <summary>
''' Convert a class state into XML
''' </summary>
''' <typeparam name="T">The type of object</typeparam>
''' <param name="obj">The object to serilize</param>
''' <param name="sConfigFilePath">The path to the XML</param>
Public Shared Sub Serialize(Of T)(ByVal obj As T, sConfigFilePath As String)
Dim XmlBuddy As New System.Xml.Serialization.XmlSerializer(GetType(T))
Dim MySettings As New System.Xml.XmlWriterSettings()
MySettings.Indent = True
MySettings.CloseOutput = True
Dim MyWriter As System.Xml.XmlWriter = System.Xml.XmlWriter.Create(sConfigFilePath,MySettings)
XmlBuddy.Serialize(MyWriter,obj)
MyWriter.Flush()
MyWriter.Close()
End Sub
''' <summary>
''' Restore a class state from XML
''' </summary>
''' <typeparam name="T">The type of object</typeparam>
''' <param name="xml">the path to the XML</param>
''' <returns>The object to return</returns>
Public Shared Function Deserialize(Of T)(ByVal xml As String) As T
Dim XmlBuddy As New System.Xml.Serialization.XmlSerializer(GetType(T))
Dim fs As New FileStream(xml, FileMode.Open)
Dim reader As New Xml.XmlTextReader(fs)
If XmlBuddy.CanDeserialize(reader) Then
Dim tempObject As Object = DirectCast(XmlBuddy.Deserialize(reader), T)
reader.Close()
Return tempObject
Else
Return Nothing
End If
End Function
end class
The call of the Serializer:
Try
XmlSerializer.Serialize(Of list(Of Statistic))(StatCollection, CommCtrl.PathStatisticFile)
Catch ex As Exception
msgbox(ex.Message)
End Try
The call of the deSerializer:
Try
StatCollection = XmlSerializer.Deserialize(Of list(Of Statistic)(CommCtrl.PathStatisticFile)
Catch ex As Exception
msgbox(ex.Message)
end Try
I needed to do this also but create a string instead. Here's my solution:
Public Shared Function Serialize(Of T)(ByVal obj As T) As String
Dim xml As New System.Xml.Serialization.XmlSerializer(GetType(T))
Dim ns As New System.Xml.Serialization.XmlSerializerNamespaces()
ns.Add("", "") 'No namespaces needed.
Dim sw As New IO.StringWriter()
xml.Serialize(sw, obj, ns)
If sw IsNot Nothing Then
Return sw.ToString()
Else
Return ""
End If
End Function
Public Shared Function Deserialize(Of T)(ByVal serializedXml As String) As T
Dim xml As New System.Xml.Serialization.XmlSerializer(GetType(T))
Dim sr As New IO.StringReader(serializedXml)
Dim obj As T = CType(xml.Deserialize(sr), T)
Return obj
End Function