Introduction to Serialization with XML
Introduction to Serialization with XML
Fundamentals of Serialization
Introduction to Values
Although many applications use local values from variables declared in those applications, the most important aspect of a database application is to give its users the ability to create new records, to verify the existence of a certain record, to open one or more existing records, to edit or update outdated records, and to delete records. There are free and commercial database environments to help you create formal applications. In some cases, you may want to provide some functionalities of a database to your application. You have many options.
Practical Learning: Introducing Serializations
Introduction to Serialization
One way you can make it possible for users of your application to create and save records is through serialization. Once a user has one or more values in your application, serialization is the ability to pass such (a) value(s) to a medium or stream. This means that serialization allows you:
The .NET Framework easily supports serialization with various options trough XML and JSON (JavaScript). You will choose which solution suits your needs.
XML Serialization
Introduction
The extensive markup language, or XML, is a technique to format a text-based document so that the document can be read by various types of systems or applications. XML is not a technology and it is not a library. It is just a way to format the document. One way to save a document in an application is to use a technique referred to as XML serialization. Although the name includes "XML", you don't need to know a single thing about XML in order to use XML serialization. This means that, to use XML serialization, don't go around trying to understand XML; it is not necessary.
To suppoprt XML serialization, the .NET Framework has a namespace named System.Xml.Serialization. That namespace contains many interfaces, classes, and structures used for various serialization scenarios. Therefore, in a document in which you want to perform XML serialization, include this namespace. This can be done as follows:
using System.Xml.Serialization;
namespace Exercises
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
}
}
The primary class used to perform XML serialization is called XmlSerializer. You can use it to declare a variable for XML serialiation. Of course, you can first write using System.Xml.Serialization; in the top section of the document. This can be done as follows:
using System.Xml.Serialization; namespace Exercises { public partial class Exercise : Form { public Exercise() { XmlSerializer xsSomething; InitializeComponent(); } } }
You can omit the using System.Xml.Serialization; line in the top section of the document but use System.Xml.Serialization.XmlSerializer to declare the variable. This can be done as follows:
namespace Exercises
{
public partial class Exercise : Form
{
public Exercise()
{
using System.Xml.Serialization.XmlSerializer xsSomething;
InitializeComponent();
}
}
}
The Type to Serialize
We consider a value as the simplest unit you can get in an application. Therefore, we consider a value as a number (int, float, double, etc), a symbol (char), a logical representation (a bool type), a name (string) or the like. Of course, whenever you are programming, you always need to know the type of value you are dealing with. As a reminder, to assist you to identify the type of a value, the .NET Framework provides an abstract class named Type. Because this information is very valuable at times, to help you, the C# language provides the typeof operator.
To let you perform serialization, the XmlSerializer class provides many constructors, one of which is a default (parameter-less) constructor, that you will hardly use. Another constructor uses the following syntax:
public XmlSerializer (Type type);
When starting a serialization operation, the XmlSerializer object wants to know the type of value you are serializing. You can use the typeof() operator to provide this information. Pass the type in the parentheses of the operator and pass it to the XmlSerializer() constructor. This can be done as follows:
using System.Xml.Serialization;
namespace FourCornerSolutions
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void Exercise_Load(object sender, EventArgs e)
{
XmlSerializer xsAppliance = new XmlSerializer(typeof(bool));
}
}
}
Writing to a Stream
The File that Holds a Stream
When performing serializtion, you must create a file that would hold the value(s) you want to send or save. It is a regular text-based file. Normally, the file can have any extension you want or you can design a customized system so that your files use, and your application supports, a specific extension. But if you are performing XML serialization, you should use the .xml extension for your serialized file(s) (again, to perform XML serialization, you don't need to know anything about XML).
To specify a file for your serialization, you can declare a string variable and initialize it with a file name. You can also initialize the variable with a complete path if you know where the file(s) will be saved or if you have already created a folder for it (them). Here is an example:
namespace FourCornerSolutions
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void Exercise_Load(object sender, EventArgs e)
{
string strAppliances = @"G:\Appliance Store\Appliances.xml";
}
}
}
In some cases, you may want to save your records in one of the folders that are created by the computer (the operating system) when it is being set up. The most common of these folders is My Documents. You may remember that, to access one those folders, you can use the Environment static class or the Environment.SpecialFolder enumeration.
Getting File Information
As you may know already, a better way to initialize a file is through the FileInfo class:
public sealed class FileInfo : System.IO.FileSystemInfo
The FileInfo class is based on the FileSystemInfo. That is where it gets most of its functionalities. The FileInfo class has one constructor that takes a file name or path as argument:
public FileInfo (string fileName);
Based on this, you can declare a variable of this class and initialize it with this constructor. This can be done as follows:
namespace FourCornerSolutions
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void Exercise_Load(object sender, EventArgs e)
{
string strAppliances = @"G:\Appliance Store\Appliances.xml";
}
}
}
After declaring the variable, to get some information about the file, you can access one of the class's members, including the members it inherits from its parent. These include the Name, the FullName, the Extension, or the Directory properties.
Checking the Existense of a File
Sometimes, before performing an operation on a file, you may want to first check whether the file exists already. You have many options. You can call the File.Exists() method to which you will pass the name or path of the file, or you can access the FileInfo.Exist property that was already initialized with a file name or path.
Starting a Stream
As mentioned already, serialization consists of sending one or more values to a medium, also called a stream. To support file processing or streaming, the .NET Framework provides the System.IO namespace.
To support streams, the System.IO namespace provides an abstract class named Stream:
public abstract class Stream : MarshalByRefObject, IAsyncDisposable, IDisposable
As you can see, Stream is an abstract class. This means that, in order to use it, you need a class derived from it. An example of such a class is FileStream
Writing Text to a Stream
We also mentioned that serialization can be done as XML, which is primarily a text format. In most cases, serialization consists of sending or writing text to a medium. To help you write text, the .NET Framework provides many classes. One of the fundamental classes for text writing is named TextWriter:
public abstract class TextWriter : MarshalByRefObject, IAsyncDisposable, IDisposable
Since TextWriter is an abstract class, in order to create text from it, you must use a class derived from it. One of the available classes is StreamWriter. Therefore, you can declare a TextWriter variable and initialize it with StreamWriter. The StreamWriter class is equipped with many constructors (10). One of the most fundamental constructors uses the following syntax:
public StreamWriter (string path);
This constructor takes the name of a file or its path as argument. Here is an example of calling it:
using System.Xml.Serialization;
namespace FourCornerSolutions
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void Exercise_Load(object sender, EventArgs e)
{
TextWriter twAppliance = new StreamWriter(@"C:\Appliance Store\Appliances.xml");
}
}
}
To let you serialize a value, the XmlSerializer class is equipped with a method named Serialize. It is overloaded with many versions (9). One of the versions uses the following syntax:
public void Serialize (System.IO.TextWriter textWriter, object? o);
This method takes two argument. The first is a TextWriter object as we reviewed them already. The second argument is the value you want to write. Here is an example of calling this method:
using System.Xml.Serialization;
namespace FourCornerSolutions
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void Exercise_Load(object sender, EventArgs e)
{
string strAppliances = @"C:\Appliance Store\Appliances.xml";
XmlSerializer xsAppliance = new XmlSerializer(typeof(bool));
TextWriter twAppliance = new StreamWriter(strAppliances);
xsAppliance.Serialize(twAppliance, true);
}
}
}
Remember that TextWriter class implements the IDisposable interface. As result, you should (must) dismiss it when it has finished doing its job. One way to take care of this is to call the Close() method of that interface. This can be done as follows:
using System.Xml.Serialization; namespace FourCornerSolutions { public partial class Exercise : Form { public Exercise() { InitializeComponent(); } private void Exercise_Load(object sender, EventArgs e) { string strAppliances = @"C:\Appliance Store\Appliances.xml"; XmlSerializer xsAppliance = new XmlSerializer(typeof(bool)); TextWriter twAppliance = new StreamWriter(strAppliances); xsAppliance.Serialize(twAppliance, true); twAppliance.Close(); } } }
This code would produce the following document:
<?xml version="1.0" encoding="utf-8"?> <boolean>true</boolean>
In the same way, you can save any other type of primitive value. Here is an example:
using System.Xml.Serialization;
namespace FourCornerSolutions
{
public partial class Exercise : Form
{
public Exercise()
{
InitializeComponent();
}
private void btnSave_Click(object sender, EventArgs e)
{
XmlSerializer xsAppliance = new XmlSerializer(typeof(string));
TextWriter twAppliance = new StreamWriter(@"C:\Appliance Store\Appliances1.xml");
xsAppliance.Serialize(twAppliance, txtApplianceName.Text);
twAppliance.Close();
txtApplianceName.Text = string.Empty;
}
}
}
This code would produce the following document:
<?xml version="1.0" encoding="utf-8"?> <string>3-Door Refrigerator in Fingerprint Resistant Stainless Steel</string>
Serialzing by Type
Serializing a Tuple
A tuple is a combination of types used as one unit. Once you have a tuple with values, if necessary, you can serialize it. To do this, specify the tuple of the type passed to the XmlSerializer constructor. Pass the name of the tuple as the second argument to the XmlSerializer.Serialize() method. Here is an example that serializes a tuple:
using System.Xml.Serialization; namespace FourCornerSolutions { public partial class Exercise : Form { public Exercise() { InitializeComponent(); } private void btnSave_Click(object sender, EventArgs e) { (long number, string type) identifiation = (long.Parse(txtItemNumber.Text), cbxCategories.Text); XmlSerializer xsAppliance = new XmlSerializer(typeof((long, string))); TextWriter twAppliance = new StreamWriter(@"C:\Appliance Store\Appliances.xml"); xsAppliance.Serialize(twAppliance, identifiation); twAppliance.Close(); } } }
This code would produce the following document:
<?xml version="1.0" encoding="utf-8"?> <ValueTupleOfInt64String xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Item1>729413</Item1> <Item2>Refrigerator</Item2> </ValueTupleOfInt64String>
In the same way, you can create a tuple that itself contains one or more tuples, and you want to serialize that tuple. Remember to to appropriately pass the complete type of the tuple and its name. Here is an example that serializes a tuple that contains other tuples:
using System.Xml.Serialization; namespace FourCornerSolutions { public partial class Exercise : Form { public Exercise() { InitializeComponent(); } private void btnSave_Click(object sender, EventArgs e) { ((long number, string type), string name, (float height, float width, float depth), double price) identifiation = ((long.Parse(txtItemNumber.Text), cbxCategories.Text), txtApplianceName.Text, (float.Parse(txtHeight.Text), float.Parse(txtWidth.Text), float.Parse(txtDepth.Text)), double.Parse(txtUnitPrice.Text)); XmlSerializer xsAppliance = new XmlSerializer(typeof(((long number, string type), string name, (float height, float width, float depth), double price))); TextWriter twAppliance = new StreamWriter(@"C:\Appliance Store\Appliances.xml"); xsAppliance.Serialize(twAppliance, identifiation); twAppliance.Close(); txtWidth.Text = string.Empty; txtDepth.Text = string.Empty; txtHeight.Text = string.Empty; txtUnitPrice.Text = string.Empty; txtItemNumber.Text = string.Empty; txtApplianceName.Text = string.Empty; } } }
This code would produce the following document:
<?xml version="1.0" encoding="utf-8"?> <ValueTupleOfValueTupleOfInt64StringStringValueTupleOfSingleSingleSingleDouble xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Item1> <Item1>927380</Item1> <Item2>Refrigerator</Item2> </Item1> <Item2>3-Door Refrigerator in Fingerprint Resistant Stainless Steel</Item2> <Item3> <Item1>70</Item1> <Item2>35.75</Item2> <Item3>35.4</Item3> </Item3> <Item4>1497.95</Item4> </ValueTupleOfValueTupleOfInt64StringStringValueTupleOfSingleSingleSingleDouble>
Serializing an Object
An object is a list of values created from a class, a record, or a structure. To perform serialization, you must start from a class, a record, or a structure. You can use one of the .NET built-in classes or you can create your own class, record, or structure. You can then first create an object from the class, record, or structure. When declaring an XmlSerializer variable, pass the name of the class/record/structure as the type passed to XmlSerializer. You can then pass the object as the second argument to the XmlSerializer.Serialize() method. You can use a simple class/record/structure that contains only properties. Here is an example that serializes an object:
using System.Xml.Serialization; namespace MachinesWorldwide { public partial class Exercise : Form { public Exercise() { InitializeComponent(); } private void Exercise_Load(object sender, EventArgs e) { Tractor? machine = new Tractor() { ModelName = "1023e", EnginePower = 16.5f, LiftCapacity = 309, CruiseControl = "No", Price = 15_580m }; XmlSerializer xsMachine = new XmlSerializer(typeof(Tractor)); TextWriter twMachine = new StreamWriter(@"C:\Machines Worldwide\Machine.xml"); xsMachine.Serialize(twMachine, machine); twMachine.Close(); } } public readonly record struct Tractor { public string? ModelName { get; init; } public float EnginePower { get; init; } public int LiftCapacity { get; init; } public string? CruiseControl { get; init; } public decimal Price { get; init; } } }
This code would produce the following document:
<?xml version="1.0" encoding="utf-8"?> <Tractor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <ModelName>1023e</ModelName> <EnginePower>16.5</EnginePower> <LiftCapacity>309</LiftCapacity> <CruiseControl>No</CruiseControl> <Price>15580</Price> </Tractor>
Serializing a Nested Object
When creating a class, record, or structure whose objects you are planning to serialize, you can add any allowable member (especially property) in it. For example, a property can be a tuple type. Here is an example for a record structure that contains a Boolean member and a tuple property:
using System.Xml.Serialization; namespace MachinesWorldwide { public partial class Exercise : Form { public Exercise() { InitializeComponent(); } private void Exercise_Load(object sender, EventArgs e) { Tractor? machine = new Tractor() { ModelName = "5050E", EnginePower = 50, TransmissionType = Standard, CruiseControl = true, Engine = (2100, EngineType.Diesel, 3), Price = 25_458 }; XmlSerializer xsMachine = new XmlSerializer(typeof(Tractor)); TextWriter twMachine = new StreamWriter(@"C:\Appliance Store\Machine.xml"); xsMachine.Serialize(twMachine, machine); twMachine.Close(); } } public enum EngineType { Gas, Diesel, Electric, Unknown } public readonly record struct Tractor { public string? ModelName { get; init; } public int EnginePower { get; init; } public string TransmissionType { get; init; public bool CruiseControl { get; init; } public (int ratedSpeed, EngineType type, int cylinders) Engine { get; init; } public decimal Price { get; init; } } }
This code would produce:
<?xml version="1.0" encoding="utf-8"?> <Tractor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <ModelName>5050E</ModelName> <EnginePower>50</EnginePower> <TransmissionType>Standard</TransmissionType> <MowerHeight>60</MowerHeight> <CruiseControl>true</CruiseControl> <Engine> <Item1>2100</Item1> <Item2>Diesel</Item2> <Item3>3</Item3> </Engine> <Price>25458</Price> </Tractor>
In the same way, a property can be an enumeration, class, record, or structure type. Here are examples:
public enum EngineType { Gas, Diesel, Electric, Unknown } public enum CruiseControl { Standard, True, Optional, NotAvailable, Unknown } public struct Capacity { public int Kilogram { get; set; } public int Pounds { get; set; } } public struct Engine { public float Power { get; set; } public EngineType Type { get; set; } public int Cylinders { get; set; } } public readonly record struct Tractor { public string? ModelName { get; init; } // A property of a class, record, or structure type public Capacity LiftCapacity { get; init; } // A property of an enumeration type public CruiseControl CruiseControl { get; init; } // A property of a class, record, or structure type public Engine EngineType { get; init; } public decimal Price { get; init; } }
If you create a tuple in a class, record, or structure, when an object from it is serialized, the compiler refers to the members of the tuple as Item1, Item2, etc (again, you are neither asked nor required to know anything about XML in order to use XML serializtion). An alternative is to create properties as class, record, or structure types.
Serializing an Array or a List
Normally, the most important characteristic of serialization is the ability to save a list. This is the basis of a database. Although the operation sounds difficult as first glance, it is extremely easy thanks to XML serialization. For a simple list, you can start by creating an array, such as an array of regular values (numbers, symbols, strings, Boolean values, etc). When declaring an XmlSerializer variable, pass a combination of the data type of the array and empty square brackets to XmlSerializer. When calling the XmlSerializer.Serialize() method, you can pass the array variable as the second argument to the method. Here is an example that serializes an array:
using System.Xml.Serialization; namespace PayrollPreparation { public partial class Exercise : Form { public Exercise() { InitializeComponent(); } private void Exercise_Load(object sender, EventArgs e) { double[] timesWorked = { 8.00d, 9.50d, 7.00d, 10.00d, 8.50d }; XmlSerializer xsTimeSheet = new XmlSerializer(typeof(double[])); TextWriter twTimeSheet = new StreamWriter(@"C:\Payroll and Accounting\TimeWorked.xml"); xsTimeSheet.Serialize(twTimeSheet, timesWorked); twTimeSheet.Close(); } } }
This code would produce:
<?xml version="1.0" encoding="utf-8"?> <ArrayOfDouble xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <double>8</double> <double>9.5</double> <double>7</double> <double>10</double> <double>8.5</double> </ArrayOfDouble>
You may also want to serialize a list of objects. Once again, you can use a built-in class (or structure) or your own-created class, record, or structure. Then, you can create an array as your list of objects. Everything else is done as seen above. Here is an example:
using System.Xml.Serialization; namespace FourCornerSolutions { public partial class Exercise : Form { public Exercise() { InitializeComponent(); } private void Exercise_Load(object sender, EventArgs e) { Tractor[] machines = { new Tractor() { ModelName = "1023E", LiftCapacity = (344, 758), CruiseControl = CruiseControl.True, Engine = (21.5f, EngineType.Diesel, 3), Price = 13_050m }, new Tractor() { ModelName = "2025R", LiftCapacity = (415, 915) , CruiseControl = CruiseControl.Standard, Engine = (23.9f, EngineType.Diesel, 3), Price = 22_055m }, new Tractor() { ModelName = "3025E", LiftCapacity = (615, 1356), CruiseControl = CruiseControl.Optional, Engine = (24.4f, EngineType.Diesel, 3), Price = 21_945m }, }; XmlSerializer xsMachine = new XmlSerializer(typeof(Tractor[])); TextWriter twMachine = new StreamWriter(@"C:\Appliance Store\Machines.xml"); xsMachine.Serialize(twMachine, machines); twMachine.Close(); } } public enum EngineType { Gas, Diesel, Electric, Unknown } public enum CruiseControl { Standard, True, Optional, NotAvailable, Unknown } public readonly record struct Tractor { public string? ModelName { get; init; } public (int kg, int lb) LiftCapacity { get; init; } public CruiseControl CruiseControl { get; init; } public (float enginePower, EngineType type, int cylinders) Engine { get; init; } public decimal Price { get; init; } } }
This code would produce:
<?xml version="1.0" encoding="utf-8"?> <ArrayOfTractor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Tractor> <ModelName>1023E</ModelName> <LiftCapacity> <Item1>344</Item1> <Item2>758</Item2> </LiftCapacity> <CruiseControl>True</CruiseControl> <Engine> <Item1>21.5</Item1> <Item2>Diesel</Item2> <Item3>3</Item3> </Engine><Price>13050</Price> </Tractor> <Tractor> <ModelName>2025R</ModelName> <LiftCapacity> <Item1>415</Item1> <Item2>915</Item2> </LiftCapacity> <CruiseControl>Standard</CruiseControl> <Engine> <Item1>23.9</Item1> <Item2>Diesel</Item2> <Item3>3</Item3> </Engine> <Price>22055</Price> </Tractor> <Tractor> <ModelName>3025E</ModelName> <LiftCapacity> <Item1>615</Item1> <Item2>1356</Item2> </LiftCapacity> <CruiseControl>Optional</CruiseControl> <Engine> <Item1>24.4</Item1> <Item2>Diesel</Item2> <Item3>3</Item3> </Engine> <Price>21945</Price> </Tractor> </ArrayOfTractor>
Some Details on XML Serialization
Finally Closing a Stream
When it comes to serialization, there are a few details you should keep in mind. Remember that the TextWriter abstract class implements the IDisposable interface because the classes derived from TextWriter consume resources while they are writing to a stream. As a result, it is a good idea to process the object of the class in a try block and close it in a finally clause. This can be done as follows:
using System.Xml.Serialization; namespace ApplianceStore { public partial class Inventory: Form { public Inventory: () { InitializeComponent(); } private void Inventory: _Load(object sender, EventArgs e) { Appliance[] appliances = { new Appliance() { ItemNumber = 314930, Dimensions = (16.9f, 20.4f, 12.4f), Make = "BLACK+DECKER", Type = "Microwave Oven", Name = "Digital Microwave Oven with Turntable Push-Button Door, Child Safety Lock, Stainless Steel", Price = 166.84, Capacity = (1.4f, "Cu.ft"), Wattage = 1000 }, new Appliance() { Make = "Instant Pot", Price = 82.37, ItemNumber = 973947, Type = "RiceCooker", Name = "Duo 7-in-1 Electric Pressure Cooker, Slow Cooker, Rice Cooker, Steamer, Sauté, Yogurt Maker, Warmer & Sterilizer", Dimensions = (13.38f, 12.2f, 12.48f), Capacity = (5.68f, "Liters"), Wattage = 1000 }, new Appliance() { Capacity = (6, "Cups"), Make = "Hamilton Beach", Price = 49.85, Type = "Deep Fryer", Name = "Cool Touch Sides Easy to Clean Nonstick Basket", ItemNumber = 202793, Dimensions = (13.13f, 11.38f, 8.58f) }, new Appliance() { Type = "Microwave Oven", Make = "Farberware", Price = 166.84, ItemNumber = 640300, Name = "Microwave Oven With Smart Sensor Cooking and LED Lighting, Brushed Stainless Steel", Dimensions = (17.3f, 20.5f, 12.8f), Capacity = (1.2f, "Cu.ft"), Wattage = 1100 }, new Appliance() { Dimensions = (13.78f, 10.2f, 8.56f), Make = "Oster", Type = "Blender", Name = "6-Cup Glass Jar 12-Speed Blender", Price = 58.77, ItemNumber = 828628, Capacity = (6, "Cups"), Wattage = 700 } }; XmlSerializer xsAppliances = new XmlSerializer(typeof(Appliance[])); TextWriter twAppliances = new StreamWriter(@"C:\Appliance Store\Appliances2.xml"); try { xsAppliances.Serialize(twAppliances, appliances); } finally { twAppliances.Close(); } } } public record class Appliance { public long ItemNumber { get; set; } public string? Type { get; set; } public string? Make { get; set; } public string? Name { get; set; } public (float depth, float width, float height) Dimensions { get; set; } public (float value, string unit) Capacity { get; set; } public int Wattage { get; set; } public double Price { get; set; } } }
Creating a Stream
As mentioned already, the .NET Framework provides the Stream abstract class that is a parent to class used to create a stream. When using this class, you need a class that is based on Stream. Probably the primary class you can use is FileStream. Therefore, you can declare a Stream variable and initialize it with FileStream. The FileStream class is equipped with various constructors. One of them takes a file name/path as argument. With this approach, you don't need a text writer. After creating a Stream object, you can call the XmlSerializer.Serialize() in a try block and close the Stream object in a finally clause. This can be done as follows:
using System.Xml.Serialization;
namespace ApplianceStore
{
public partial class Inventory: Form
{
public Inventory: ()
{
InitializeComponent();
}
private void Inventory: _Load(object sender, EventArgs e)
{
Appliance[] appliances =
{
// . . .
};
XmlSerializer xsAppliances = new XmlSerializer(typeof(Appliance[]));
Stream fsAppliances = new FileStream(@"C:\Appliance Store\Appliances.xml", FileMode.Create, FileAccess.Write, FileShare.Write);
try
{
xsAppliances.Serialize(fsAppliances, appliances);
}
finally
{
fsAppliances.Close();
}
}
}
public record class Appliance
{
// . . .
}
}
Using a Stream
using System.Xml.Serialization;
namespace ApplianceStore
{
public partial class Inventory: Form
{
public Inventory: ()
{
InitializeComponent();
}
private void Inventory: _Load(object sender, EventArgs e)
{
Appliance[] appliances =
{
// . . .
};
XmlSerializer xsAppliances = new XmlSerializer(typeof(Appliance[]));
using (Stream fsAppliances = new FileStream(@"C:\Appliance Store\Appliances.xml", FileMode.Create, FileAccess.Write, FileShare.Write))
{
xsAppliances.Serialize(fsAppliances, appliances);
}
}
}
public record class Appliance
{
// . . .
}
}
Introduction to Deserialization
Overview
Serialization consists of sending or writing one or more values to a stream. Here is an example as we saw already:
using System.Xml.Serialization; namespace FourCornerSolutions { public partial class SerializationDetails : Form { public SerializationDetails() { InitializeComponent(); } private void btnSerialization_Click(object sender, EventArgs e) { string strAppliances = @"C:\Appliance Store\Appliance.xml"; XmlSerializer xsAppliance = new XmlSerializer(typeof(string)); using(TextWriter twAppliance = new StreamWriter(strAppliances)) xsAppliance.Serialize(twAppliance, "Deep Fryer with Basket, Stainless Steel"); } } }
The opposite of serialization is to read or get values that exist on a stream. This operation is called deserialization. As seen with serialization, to perform deserialization, you have many options.
As always done in file processing, to start, you must identify the file that holds the values you want to ready. You can declare a string variable and initialize it with the name or path of the file. Once again, to start the operation of serialization, you can declare an XmlSerializer variable and pass its type as argument to its constructor. Here is an example:
using System.Xml.Serialization;
namespace FourCornerSolutions
{
public partial class SerializationDetails : Form
{
public SerializationDetails()
{
InitializeComponent();
}
private void btnDeserialization_Click(object sender, EventArgs e)
{
XmlSerializer xsAppliance = new XmlSerializer(typeof(string));
}
}
}
Reading from a File
We already know that the .NET Framework supports file processing through the System.IO namespace. If you write values using the TextWriter class or a class derived from it, to let you read values from a stream, the .NET Framework provides a class named TextReader:
public abstract class TextReader : MarshalByRefObject, IDisposable
Notice that this is an abstract class. To read from a medium, you can use a class derived from TextReader. One of those is named StreamReader:
public class StreamReader : System.IO.TextReader
The StreamReader class is equipped with many (13) constructors. One of the constructors uses the following syntax:
public StreamReader (string path);
This constructor takes as string the name of a file or its path. Based on this, you can declare a TextReader variable, initialize it with a StreamReader constructor, and pass the file name or path to it. Here is an example:
using System.Xml.Serialization;
namespace FourCornerSolutions
{
public partial class SerializationDetails : Form
{
public SerializationDetails()
{
InitializeComponent();
}
private void btnDeserialization_Click(object sender, EventArgs e)
{
XmlSerializer xsAppliance = new XmlSerializer(typeof(string));
TextReader trAppliance = new StreamReader(@"C:\Appliance Store\Appliance.xml");
}
}
}
The StreamReader class is equipped with another constructor that uses the following syntax:
public StreamReader (System.IO.Stream stream);
This version of the constructor expects a Stream object or an object from a class derived from Stream as argument.
As you should know by now, when you have finished using a Stream-based object, you should dismiss it. This can be done by calling the object's Close() method. Hee is an example:
using System.Xml.Serialization;
namespace FourCornerSolutions
{
public partial class SerializationDetails : Form
{
public SerializationDetails()
{
InitializeComponent();
}
private void btnDeserialization_Click(object sender, EventArgs e)
{
TextReader trAppliance = new StreamReader(@"C:\Appliance Store\Appliance.xml");
trAppliance.Close();
}
}
}
Another solution is to invoke the using() operator of the C# language.
Deserializing a Value
As mentioned already, deserialization consists of getting or reading one or more values from a stream. To support deserialization, the XmlSerializer class is equipped with a method named Deserialize. It is provided in many versions (7). One of the versions uses the following syntax:
public object? Deserialize (System.IO.TextReader textReader);
This version expexts a TextReader type of object. Once you pass such an object, the method would get the value(s) stored in the file communicated to the text reader and the method would return that (or those) value(s). The returned value(s) is (are) produced as an object. This means that the returned value can be anything. That's why, when creating an XmlSerializer object, you must indicate the type of value you are reading. In the same way, you must case the returned value of the XmlSerializer.Deserialize() method to the type you passed to the XmlSerializer() constructor. If the XmlSerializer.Deserialize() method succeeds in reading the value, it returns it. You can then use the returned value as you see fit. Here is an example:
using System.Xml.Serialization;
namespace FourCornerSolutions
{
public partial class SerializationDetails : Form
{
public SerializationDetails()
{
InitializeComponent();
}
private void btnSerialization_Click(object sender, EventArgs e)
{
string strAppliances = @"C:\Appliance Store\Appliance1.xml";
XmlSerializer xsAppliance = new XmlSerializer(typeof(string));
using(TextWriter twAppliance = new StreamWriter(strAppliances))
xsAppliance.Serialize(twAppliance, "Deep Fryer with Basket, Stainless Steel");
}
private void btnDeserialization_Click(object sender, EventArgs e)
{
string strAppliance = @"C:\Appliance Store\Appliance1.xml";
XmlSerializer xsAppliance = new XmlSerializer(typeof(string));
TextReader trAppliance = new StreamReader(strAppliance);
string strResult = (string)xsAppliance.Deserialize(trAppliance)!;
trAppliance.Close();
MessageBox.Show(strResult);
}
}
}
Another version of the XmlSerializer.Deserialize() method uses the following suntax:
public object? Deserialize (System.IO.Stream stream);
This version expects a Stream-based object as argument. If you decide to use this version, then you don't need a text reader.
Deserializing an Object
We already saw how to serialize an object. Here is an example:
using System.Xml.Serialization; namespace FourCornerSolutions { public partial class Exercise : Form { public Exercise() { InitializeComponent(); } private void btnSerialization_Click(object sender, EventArgs e) { Tractor? machine = new Tractor() { ModelName = "MW9724", EnginePower = 21.5f, LiftCapacity = 754, MowerHeight = 60, Price = 13_050 }; XmlSerializer xsMachine = new XmlSerializer(typeof(Tractor)); using(TextWriter twMachine = new StreamWriter(@"C:\Four Corner Solutions\Machine.xml")) xsMachine.Serialize(twMachine, machine); } } public readonly record struct Tractor { public string? ModelName { get; init; } public float EnginePower { get; init; } public int LiftCapacity { get; init; } public int MowerHeight { get; init; } public decimal Price { get; init; } } }
To deserialize an object, pass its class to the XmlSerializer() variable. Declare a variable of your class and assign a call to the XmlSerializer.Deserialize() method to it. You must pass the means by which you will read the values to the XmlSerializer.Deserialize() method. Make sure you cast that method call to your class. With that variable, you get an object of your class, and you can use that object as you want. Here is an example:
using System.Xml.Serialization; namespace FourCornerSolutions { public partial class SerializationDetails : Form { public SerializationDetails() { InitializeComponent(); } private void btnDeserialization_Click(object sender, EventArgs e) { string strMachine = @"C:\Four Corner Solutions\Machine.xml"; XmlSerializer xsMachine = new XmlSerializer(typeof(Tractor)); TextReader trMachine = new StreamReader(strMachine); Tractor machine = (Tractor)xsAppliance.Deserialize(trAppliance)!; txtModelName.Text = machine.ModelName; txtEnginePower.Text = machine.EnginePower.ToString("n"); txtLiftCapacity.Text = machine.LiftCapacity.ToString(); txtMowerHeight.Text = machine.MowerHeight.ToString(); txtPrice.Text = machine.Price.ToString("n"); trMachine.Close(); } } public readonly record struct Tractor { public string? ModelName { get; init; } public float EnginePower { get; init; } public int LiftCapacity { get; init; } public int MowerHeight { get; init; } public decimal Price { get; init; } } }
Practical Learning: Ending the Lesson
Previous | Copyright © 2010-2023, FunctionX | Monday 06 December 2021 | Next |