473,406 Members | 2,849 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes and contribute your articles to a community of 473,406 developers and data experts.

Basic XML Serialization in VB.NET

!NoItAll
297 100+
Reading XML in VB.NET is pretty straightforward. Create an XML Document Object, then you can load the XML and iterate through all of the nodes as required. You can search for nodes, use x-path, whatever you like.
Creating XML, on the other hand, is not so straight forward. There are essentially two methods.
  • Create an XML Document Object and set each of the Element and/or attribute nodes to the values you want
  • Create the structure you want with serialization in mind and serialize the data to XML

Option 1 is, in my opinion, a non-option. While it may be easier to create your structures without having to think of serialization, the process of creating the XML Document Object (that's not hard) and then creating and setting each of the Element and Attribute nodes is suicidal! It also will create a code segment that will collapse under its own weight, even with less-than-complex XML! I have a project I did prior to learning serialization and the process of saving the XML takes about 3 pages of code, and I can't understand it any more!

Enter XML Serialization
I am not going to go into excrutiating detail on all of this, instead I have created a VS 2008 project you can download and play with. In it you will see the following things:
  • A few classes that are designed to create the data structure I wish to create as XML and read in as XML.
  • A way to name the XML nodes anything you want
  • A way to keep the XML clean (by default .NET will load all kinds of namespace garbage into your XML that doesn't need to be there)
  • How to save the Data Object as XML using a generic serialization function
  • How to read the XML into the Data Object using a generic serialization function
I think that covers it...

I am going to create a simple structure for Employees. This structure will consist of a four basic data points - one of which will be a nested structure with three data points of its own.
Employee
FirstName
LastName
Birthday
Responsibilities
ResponsibilityOne
ResponsibilityTwo
ResponsibilityThree

We will need to be able to create an array of this structure because we will have more than one employee.

We are going to start by building the structure in classes. I suppose you could do it with a simple structure - I may try that later, but today we are going to create classes. I like classes. The classes will all be inside a vb module I call Serialize. Its a dumb name - but it's only a demo folks.

Expand|Select|Wrap|Line Numbers
  1. Public Module Serialize
  2.  
  3.     Public EmploymentData As New Group
  4.  
  5.     <XmlRoot("Employees")> _
  6.     Public Class Group
  7.         <XmlElement("IndividualEmployee")> _
  8.         Public Employees() As Employee
  9.     End Class
  10.  
  11.     Public Class Employee
  12.         Private vFirstName As String = ""
  13.         Private vLastName As String = ""
  14.         Private vBirthday As New Date
  15.  
  16.         Private vPriorities As JobPriorities
  17.  
  18.         Public Sub New()
  19.         End Sub
  20.  
  21.         Public Sub New(ByVal FirstName As String, ByVal LastName As String, ByVal Birthday As Date, ByVal Responsibilities As JobPriorities)
  22.             Me.FirstName = FirstName
  23.             Me.LastName = LastName
  24.             Me.Birthday = Birthday
  25.             Me.Priorities = Responsibilities
  26.         End Sub
  27.  
  28.         <XmlAttribute("firstname")> _
  29.         Public Property FirstName() As String
  30.             Get
  31.                 Return vFirstName
  32.             End Get
  33.             Set(ByVal value As String)
  34.                 vFirstName = value
  35.             End Set
  36.         End Property
  37.  
  38.         <XmlAttribute("lastname")> _
  39.         Public Property LastName() As String
  40.             Get
  41.                 Return vLastName
  42.             End Get
  43.             Set(ByVal value As String)
  44.                 vLastName = value
  45.             End Set
  46.         End Property
  47.  
  48.         <XmlAttribute("birthday")> _
  49.         Public Property Birthday() As String
  50.             Get
  51.                 Return vBirthday
  52.             End Get
  53.             Set(ByVal value As String)
  54.                 vBirthday = value
  55.             End Set
  56.         End Property
  57.  
  58.         <XmlElement("Jobs")> _
  59.         Public Property Priorities() As JobPriorities
  60.             Get
  61.                 Return vPriorities
  62.             End Get
  63.             Set(ByVal value As JobPriorities)
  64.                 vPriorities = value
  65.             End Set
  66.         End Property
  67.  
  68.     End Class
  69.  
  70.     Partial Public Class JobPriorities
  71.  
  72.         Private vResponsibilityOne As String = ""
  73.         Private vResponsibilityTwo As String = ""
  74.         Private vResponsibilityThree As String = ""
  75.  
  76.         Public Sub New()
  77.         End Sub
  78.  
  79.         Public Sub New(ByVal ResponsibilityOne As String, Optional ByVal ResponsibilityTwo As String = "", Optional ByVal ResponsibilityThree As String = "")
  80.             Me.ResponsibilityOne = ResponsibilityOne
  81.             Me.ResponsibilityTwo = ResponsibilityTwo
  82.             Me.ResponsibilityThree = ResponsibilityThree
  83.         End Sub
  84.  
  85.         <XmlElement("first_priority")> _
  86.         Public Property ResponsibilityOne() As String
  87.             Get
  88.                 Return vResponsibilityOne
  89.             End Get
  90.             Set(ByVal value As String)
  91.                 vResponsibilityOne = value
  92.             End Set
  93.         End Property
  94.  
  95.         <XmlElement("Second_priority")> _
  96.         Public Property ResponsibilityTwo() As String
  97.             Get
  98.                 Return vResponsibilityTwo
  99.             End Get
  100.             Set(ByVal value As String)
  101.                 vResponsibilityTwo = value
  102.             End Set
  103.         End Property
  104.  
  105.         <XmlElement("Third_priority")> _
  106.         Public Property ResponsibilityThree() As String
  107.             Get
  108.                 Return vResponsibilityThree
  109.             End Get
  110.             Set(ByVal value As String)
  111.                 vResponsibilityThree = value
  112.             End Set
  113.         End Property
  114.  
  115.     End Class
  116.  
I am creating three classes.

Employee
JobPriorities
Group

Lets ignore the class decoration for now. (the decoration is all that <xmlRoot("Employees")> kind of stuff in front of each class. I'll explain that a little later.

The Group class allows me to create an array of employees, and is in fact a very small, but necessary class.

The Employee class contains the three primitive elements and one element that is of the JobPriorities class.

There's really nothing special in these classes. You can see that I did overload the New constructor - mostly just for convenience - so users would have a way to construct a new object and set the properties easily. These are very simple and straightforward classes with appropriate setters and getters. It has properly constructed private variables and public properties.

Creating the Object, Setting the Properties, and Serializing to XML
Notice at the beginning of the Serialize module I create a global object called EmploymentData. This is the object I will add data to before I serialize the output to XML.

In a button_click event I put the following code

Expand|Select|Wrap|Line Numbers
  1.     Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
  2.  
  3.         Dim MyPriorities As JobPriorities
  4.  
  5.         ReDim Preserve EmploymentData.Employees(2)
  6.  
  7.         MyPriorities = New JobPriorities("Secretary", "Eye Candy", "Mistress")
  8.         EmploymentData.Employees(0) = New Employee("Melissa", "Ryan", "1/1/1970", MyPriorities)
  9.  
  10.         MyPriorities = New JobPriorities("Janitor", "Cook", "Bottlewasher")
  11.         EmploymentData.Employees(1) = New Employee("Amanda", "Terranova", "1/1/1980", MyPriorities)
  12.  
  13.         MyPriorities = New JobPriorities("Boss", "Idiot", "Syncophant")
  14.         EmploymentData.Employees(2) = New Employee("Harlan", "Butts", "1/1/1980", MyPriorities)
  15.  
  16.         SaveXML(MyXMLFile, EmploymentData, GetType(Group))
  17.  
  18.     End Sub
  19.  
Here I am Creating a MyPriorities object from the JobPriorities class and setting the properties of the EmploymentData object with a bunch of hard coded values. I felt it was important to show some level of nesting in my example - because that can be confusing and difficult to understand for someone just starting out (like me). The EmploymentData object (which was declared Globally in the Serialize module) will have three employees - so I need to Redim it to 2 (0, 1, 2).
Expand|Select|Wrap|Line Numbers
  1. ReDim Preserve EmploymentData.Employees(2)
I now have an array of EmploymentData that I can fill out. One of the properties of the EmploymentData is going to be a JobPriorities type and I have created MyPriorities for that.

Expand|Select|Wrap|Line Numbers
  1. Dim MyPriorities As JobPriorities
Now I can fill in the structure array

Expand|Select|Wrap|Line Numbers
  1.         MyPriorities = New JobPriorities("Secretary", "Eye Candy", "Mistress")
  2.         EmploymentData.Employees(0) = New Employee("Melissa", "Ryan", "1/1/1970", MyPriorities)
  3.  
  4.         MyPriorities = New JobPriorities("Janitor", "Cook", "Bottlewasher")
  5.         EmploymentData.Employees(1) = New Employee("Amanda", "Terranova", "1/1/1980", MyPriorities)
  6.  
  7.         MyPriorities = New JobPriorities("Boss", "Idiot", "Syncophant")
  8.         EmploymentData.Employees(2) = New Employee("Harlan", "Butts", "1/1/1980", MyPriorities)
  9.  
And finally I can send it to my generic Serialization function

Expand|Select|Wrap|Line Numbers
  1.         SaveXML(MyXMLFile, EmploymentData, GetType(Group))
Here is where the heavy lifting takes place. The function to serialize the entire structure is really just a few lines of code. I kept it basic and generic so that virtually any object can be passed. I did this by having it accept anything derived from the Object base class (just about everything in VB) and returning anything derived from the Object class. When you SaveXML you pas it the filename, the Object with the data, and you have to tell it exactly what object you are sending it.

The Generic Serialization Code

Expand|Select|Wrap|Line Numbers
  1.     Public Function SaveXML(ByVal FileName As String, ByVal DataToSerialize As Object, ByVal objType As Type) As Boolean
  2.  
  3.         'set up a blank namespace to eliminate unnecessary junk from the xml
  4.         Dim nsBlank As New XmlSerializerNamespaces
  5.         nsBlank.Add("", "")
  6.  
  7.         'create an object for the xml settings to control how the xml is written and appears
  8.         Dim xSettings As New System.Xml.XmlWriterSettings
  9.         With xSettings
  10.             .Encoding = Encoding.UTF8
  11.             .Indent = True
  12.             .NewLineChars = Environment.NewLine
  13.             .NewLineOnAttributes = False
  14.             .ConformanceLevel = Xml.ConformanceLevel.Document
  15.         End With
  16.  
  17.         Try
  18.             'create the xmlwriter object that will write the file out
  19.             Dim xw As System.Xml.XmlWriter = Xml.XmlWriter.Create(FileName, xSettings)
  20.  
  21.             'create the xmlserializer that will serialize the object to XML
  22.             Dim writer As New XmlSerializer(objType)
  23.  
  24.             'now write it out
  25.             writer.Serialize(xw, DataToSerialize, nsBlank)
  26.  
  27.             'be sure to close it or it will remain open
  28.             xw.Close()
  29.  
  30.             Return True
  31.  
  32.         Catch ex As Exception
  33.  
  34.             MsgBox(ex.Message)
  35.             Return False
  36.  
  37.         End Try
  38.  
  39.  
  40.     End Function
  41.  
This code is pretty self-explanatory. The one, non-obvious, element is my creation of a new XmlSerializerNamespaces object. I did this because .NET, by default, will put some namespace declarations into your XML. It's not necessary, in only creates noise in my opinion. So by creating a blank Namespace and including that in my serializer, it will remove it.

Once it is written out the XML looks like this:

Expand|Select|Wrap|Line Numbers
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Employees>
  3.   <IndividualEmployee firstname="Melissa" lastname="Ryan" birthday="1/1/1970">
  4.     <Jobs>
  5.       <first_priority>Secretary</first_priority>
  6.       <Second_priority>Eye Candy</Second_priority>
  7.       <Third_priority>Mistress</Third_priority>
  8.     </Jobs>
  9.   </IndividualEmployee>
  10.   <IndividualEmployee firstname="Amanda" lastname="Terranova" birthday="1/1/1980">
  11.     <Jobs>
  12.       <first_priority>Janitor</first_priority>
  13.       <Second_priority>Cook</Second_priority>
  14.       <Third_priority>Bottlewasher</Third_priority>
  15.     </Jobs>
  16.   </IndividualEmployee>
  17.   <IndividualEmployee firstname="Harlan" lastname="Butts" birthday="1/1/1980">
  18.     <Jobs>
  19.       <first_priority>Boss</first_priority>
  20.       <Second_priority>Idiot</Second_priority>
  21.       <Third_priority>Syncophant</Third_priority>
  22.     </Jobs>
  23.   </IndividualEmployee>
  24. </Employees>
  25.  
This is very pretty because I set the properties of the XMLWriterSettings object I created (xSettings) to define the look of the XML.

Reading the XML into the EmploymentData object

Getting the XML back into an EmploymentData object is equally simple.

In another Button_Click event I place the following code:

Expand|Select|Wrap|Line Numbers
  1.     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
  2.  
  3.         Dim EmployeeData As New Group
  4.  
  5.         EmployeeData = GetXML(MyXMLFile, GetType(Group))
  6.  
  7.         'the following is just so you can see the output from the EmploymentData object
  8.  
  9.         Dim sData As Employee
  10.         With TextBox1
  11.             For Each sData In EmployeeData.Employees
  12.                 .SelectedText = "Employee: " & sData.FirstName & " " & sData.LastName & Environment.NewLine
  13.                 .SelectedText = "Birthday: " & sData.Birthday & Environment.NewLine
  14.                 .SelectedText = "Job Responsabilities: " & Environment.NewLine
  15.                 .SelectedText = "     " & sData.Priorities.ResponsibilityOne & Environment.NewLine
  16.                 .SelectedText = "     " & sData.Priorities.ResponsibilityTwo & Environment.NewLine
  17.                 .SelectedText = "     " & sData.Priorities.ResponsibilityThree & Environment.NewLine
  18.                 .SelectedText = "---------------" & Environment.NewLine
  19.             Next
  20.             .SelectedText = ""
  21.         End With
  22.  
  23.     End Sub
  24.  
I create a new EmployeeData object based on Group. Here I do not need to create the Array as the deserialization will create the array for me.
There's also a bunch of code that places the results into a text box just so you can see that it actually deserialized the XML and created the array.

The deserializer, like the serializer is generic. I followed the same rules and return the data in the base Object class. To use the generic deserializer you only need to tell it where the XML file is, and tell it what type of object you are expecting to deserialize.

Expand|Select|Wrap|Line Numbers
  1.     Public Function GetXML(ByVal sFileName As String, ByVal objType As Type) As Object
  2.  
  3.         If My.Computer.FileSystem.FileExists(sFileName) Then
  4.             Dim fs As FileStream = New FileStream(sFileName, FileMode.Open)
  5.             Dim xs As XmlSerializer = New XmlSerializer(objType)
  6.             Dim obj As Object = CType(xs.Deserialize(fs), Object)
  7.             fs.Close()
  8.             Return obj
  9.         Else
  10.             Return Nothing
  11.         End If
  12.  
  13.     End Function
  14.  
This works a treat!

XML Decoration

By default the XML will use your class names for node names. This may be perfectly fine with you. I wanted to override that naming in another project I am doing so I needed to figure that out. Here is the process.

When creating the classes for the Group Class you can tell the Serializer to use different names for the Elements and Attributes. By preceding each class with the appropriate decoration you will override the default behavior of the serialization code (which is to use the class names).

Since Group is my root class I decorate it with
<XmlRoot("Employees")> _
Notice in the Group Class I create one member variable array called Employees which I want to appear as XML Elements so I use the decoration:
<XmlElement("IndividualEmployees:)> _
Since it will be an array the serializer will give each Employee node the tag name of "IndividualEmployee." You can call the node whatever you like.

For each of the properties in the Employee class I decorated appropriate names, but also declared them to be Attributes. I did this with the following decoration:
<XmlAttribute("firstname")> _
I did this with three of the four properties in the Employee class so that when Employee was serialized they would become Attributes in XML rather than new tags. You could place them in as Elements if you like, but I like the way Attributes read for this data. The Priorities Property I decided to call "Jobs" and make it an Element (because it would have sub elements). I used the following decoration there:

<XmlElement("Jobs")> _


In the JobPriorities class I used the decorations to create elements and name them appropriately.

All in all some of my renaming is probably making the code a bit more confusing, but I felt it was important to show how decorations can be used to define how properties appear as either Attributes or Elements, and that you can have them appear in the XML with any name you want. So if it's confusing - I know about it, but it was done that way as a learning tool.


I've included a full VS2008 VB.NET project you can play with. It's self contained and includes all the source referenced in this article.

Hopefully I haven't done anything stupid - but don't be shy about letting me know.

Des
Attached Files
File Type: zip TestSerialization.zip (76.1 KB, 1022 views)
Feb 28 '10 #1
2 27486
!NoItAll
297 100+
Classes are nice, but perhaps you want to do it a little more simply. Instead of the class structure I did originally - you can do it entirely with simple structures in VB.NET as well. Structures are really very similar to classes - and they appear to also be derived from the Object base class - so the generic serializer and deserializer work just fine. You can pass my generic SaveXML and ReadXML functions objects that are structure or classes.

Here are my structures
Expand|Select|Wrap|Line Numbers
  1.     <Serializable()> _
  2.     <XmlRoot("employees")> _
  3.     Public Structure T_Group
  4.         <XmlElement("employee")> Public Employees() As T_Employee
  5.     End Structure
  6.  
  7.     Public Structure T_Employee
  8.         <XmlAttribute("firstname")> Public FirstName As String
  9.         <XmlAttribute("lastname")> Public LastName As String
  10.         <XmlAttribute("birthday")> Public Birthday As Date
  11.         <XmlElement("jobs")> Public Jobs As T_Jobs
  12.     End Structure
  13.  
  14.     Public Structure T_Jobs
  15.         <XmlElement("first_priority")> Public ResponsibilityOne As String
  16.         <XmlElement("second_priority")> Public ResponsibilityTwo As String
  17.         <XmlElement("third_priority")> Public ResponsibilityThree As String
  18.     End Structure
  19.  
As you can see there's a lot less code than in the classes, but it's also not as powerful. Sometimes you just don't need the power though.
Same decoration rules apply.
I've uploaded the original project with the structure approach added to it. You can look at both.
Again - if you see stupidity I'm not to proud to admit it - so let me know if you see better ways of doing things.

Des
Attached Files
File Type: zip TestSerialization_withStruct.zip (124.6 KB, 601 views)
Mar 1 '10 #2
sashi
1,754 Expert 1GB
Hi there,

Nice piece of work! Keep up the good job :)
Mar 1 '10 #3

Sign in to post your reply or Sign up for a free account.

Similar topics

11
by: Amadrias | last post by:
Hi all, I am using a class to transport some data over the network. I then added the attribute to the class. My problem is that this class is part of a framework and that I do not want...
0
by: uAsking | last post by:
I'm building a client side application that uses webservices. In the client application I'm inheriting from basic types used by the webservices, so the wevservice provides the classes and sets all...
8
by: Eric Eggermann | last post by:
I'm having a problem with really large file sizes when serializing the classes that describe my little document. There are some circular references which result in the same object getting written...
2
by: Dominic | last post by:
Hi everybody, I'm planning to use serialization to persist an object (and possibly its child objects) in my application. However, I'm concerned about the backward compatibility issue. I'm...
1
by: John Sutor | last post by:
Can anyone explain simply and possibly show an example of Serialization?
1
by: BFlaherty_2003 | last post by:
I have a basic serialization app. I am able to serialize my struct with varying level of success. The problem that I am seeing is that when any of the values in my class (see below) are not...
15
by: Herby | last post by:
This is a follow on from my previous thread about clr/safe and STL.NET ...
7
by: Joe | last post by:
I've tracked the performance issue down to a single class. This class derives from CollectionBase and stores a basic value type such as string, int, double, etc... I also store the type itself...
11
by: William | last post by:
I'm looking for an example that would show how to serialize a c++ object at it's simplest w/o using any other api's. I have a class that I want to serialize and then pass to my obj-c class so I can...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.