|
Another C++ XML Serialization Program Example
Create a new CLR console application. You can use the XmlSerializeCP as the project and solution names if you want.
Add the following code that includes the main(). |
// XmlSerializeCP.cpp : main project file. // This sample illustrates XML serialization. This sample illustrates using the // XmlSerializer class. Additionally, this sample illustrates simple // serialization as well as attribute and overriding serialization. The sample // illustrates both serialization and deserialization from a file. Note however, // a serialized file can only be deserialized using the same flags. That is if // the flag "/override" was specified during serialization, it must also be present // when deserializing. // // Usage: // executable_file_name [/serialize | /deserialize] // [/simple | /attribute | /override] [/file file.xml] // // /serialize Serialize data to XML // /deserialize Deserialize data from XML // /simple Perform simple XML serialization // /attribute Perform XML serialization with attributes // /override Perform XML serialization with overridden attributes // /file filename File name to serialize to or deserialize from // // Sample usage: // Simple XML serialization // executable_file_name /serialize /simple /file simple.xml // Simple XML deserialization // executable_file_name /deserialize /simple /file simple.xml // Serialize XML with overridden attributes // executable_file_name /serialize /override /file override.xml // Deserialize XML with overridden attributes // executable_file_name /deserialize /override /file override.xml //
#include "stdafx.h"
using namespace System; using namespace System::IO; using namespace System::Xml::Serialization; using namespace System::Data;
/// <summary> /// Simple enumeration for the operation to perform: deserialization or serialization. /// </summary> enum class SerializeOperation { opRead, // Deserialize opWrite // Serialize };
/// <summary> /// Enumeration indicating which class to serialize. /// </summary> enum class SerializeClass { classSimple, classAttribute, classOverride };
/// <summary> /// This is a simple helper class which creates a DataSet and initializes some data /// within it. The DataSet is an address book with two data columns: name and phone number. /// </summary> public ref class XmlAddressBookData { public: static DataSet^ CreateAddressBook() { DataSet^ addressBook; DataTable^ friendsTable; DataColumn^ infoColumn; DataRow^ friendEntry;
// Create a collection, first create the dataset and then the table contained in the dataset Console::WriteLine("Creating a collection..."); addressBook = gcnew DataSet("AddressBook"); friendsTable = gcnew DataTable("Friends");
// Add columns to the data set Console::WriteLine("Adding columns..."); infoColumn = gcnew DataColumn("Name"); friendsTable->Columns->Add(infoColumn); infoColumn = gcnew DataColumn("Number"); friendsTable->Columns->Add(infoColumn); addressBook->Tables->Add(friendsTable);
// Add a few entries to the collection Console::WriteLine("Adding entries..."); friendEntry = friendsTable->NewRow(); friendEntry[0] = "Jim"; friendEntry[1] = "867-5309"; friendsTable->Rows->Add(friendEntry);
friendEntry = friendsTable->NewRow(); friendEntry[0] = "Lance"; friendEntry[1] = "555-4567"; friendsTable->Rows->Add(friendEntry);
friendEntry = friendsTable->NewRow(); friendEntry[0] = "Anthony"; friendEntry[1] = "555-1234"; friendsTable->Rows->Add(friendEntry);
return addressBook; }
/// <summary> /// Routine for displaying the contents of the address book. /// </summary> /// <param name="addressBook">Dataset to print</param> public: static void PrintAddressBook(DataSet^ addressBook) { for each (DataTable^ table in addressBook->Tables) { for each (DataRow^ row in table->Rows) { Console::WriteLine("Name {0}: {1}", row["Name"], row["Number"]); } } } };
/// <summary> /// Simple data structure to be serialize using the XML formatter. This structure contains a collection. /// </summary> public ref class MyXmlSimpleData { public: // Preventing the DateTime class from serialization // XmlSerializer will skip over it. It is failed to serialize DateTime in C++/CLI // Use [XmlIgnore] attribute instead for the XmlSerializer to simply ignore this field. [XmlIgnoreAttribute] DateTime^ CurrentDate; DataSet^ MyAddressBook; array<String^>^ DaysOfTheWeek;
// Private properties won't be serialized private: int MySalary;
/// <summary> /// Default constructor for the MyXmlSimplData class which initializes /// the fields and builds a simple collection of names and phone numbers. /// </summary> public: MyXmlSimpleData() { CurrentDate = DateTime::Today; MySalary = 0; MyAddressBook = XmlAddressBookData::CreateAddressBook();
DaysOfTheWeek = gcnew array<String^>(7); DaysOfTheWeek[0] = "Sunday"; DaysOfTheWeek[1] = "Monday"; DaysOfTheWeek[2] = "Tuesday"; DaysOfTheWeek[3] = "Wednesday"; DaysOfTheWeek[4] = "Thursday"; DaysOfTheWeek[5] = "Friday"; DaysOfTheWeek[6] = "Saturday"; }
/// <summary> /// Simple method to set the private fields value. /// </summary> public: void SetSalary(int data) { MySalary = data; }
/// <summary> /// Displays the values of all member variables including the collection. /// </summary> public: void Print() { Console::WriteLine("MyXmlSimpleData::CurrentDate = {0}", CurrentDate); XmlAddressBookData::PrintAddressBook( MyAddressBook ); Console::WriteLine("MyXmlSimpleData::MySalary = {0}", MySalary);
for(int i=0; i < DaysOfTheWeek->Length ;i++) { Console::WriteLine("DaysOfTheWeek[{0}] = {1}", i, DaysOfTheWeek[i]); } }
/// <summary> /// This routine overrides the default serialization by changing the element name /// for a member variable when it is serialized. This is typically used when the /// same data needs to be serialized to XML with differing XML attributes. /// </summary> /// <param name="fileStream">The FileStream object to serialize the data to</param> public: void OverrideSerialization( FileStream^ fileStream ) { XmlElementAttribute^ xmlElementAttribute = gcnew XmlElementAttribute(); XmlAttributes^ xmlAttributes = gcnew XmlAttributes(); XmlAttributeOverrides^ xmlAttributeOverrides = gcnew XmlAttributeOverrides(); XmlSerializer^ xmlSerializer = nullptr;
// Override the element name for the "CurrentDate" property // Setting XmlIgnore to false overrides the XmlIgnoreAttribute // applied to the "CurrentDate" field, thus it will be serialized and vice versa xmlElementAttribute->ElementName = "Override_Current_Date"; xmlAttributes->XmlIgnore = true; xmlAttributes->XmlElements->Add( xmlElementAttribute ); xmlAttributeOverrides->Add( MyXmlSimpleData::typeid, "CurrentDate", xmlAttributes );
// Serialize the class except specify the XmlAttributeOverrides object as well xmlSerializer = gcnew XmlSerializer( MyXmlSimpleData::typeid, xmlAttributeOverrides );
try { Console::WriteLine("Serializing an overridden class..."); xmlSerializer->Serialize( fileStream, this ); } catch ( InvalidOperationException^ err ) { Console::WriteLine("Serialization of overridden class failed: {0}", err->Message); } } };
/// <summary> /// This class illustrates declaring XML attributes along with the class definition. /// These attributes affect how the data is serialized to XML. /// </summary> public ref class MyXmlAttributeData { public: // Using Attributes That Control XML Serialization // http://msdn.microsoft.com/en-us/library/83y7df3e(VS.71).aspx [ XmlElement( ElementName = "_1394Type" )] int FireWireType;
public: // Using Attributes That Control XML Serialization [ XmlArray( "SupportedDevices" ), XmlArrayItem( "_1394Device" ) ] array<String^>^ SupportedDevices;
/// <summary> /// Simple constructor to initialize member variables /// </summary> public: MyXmlAttributeData() { FireWireType = 1;
SupportedDevices = gcnew array<String^>(5); SupportedDevices[0] = "scanner"; SupportedDevices[1] = "camcorder"; SupportedDevices[2] = "mp3 player"; SupportedDevices[3] = "IPhone"; SupportedDevices[4] = "USB drive"; }
/// <summary> /// Prints the class members to the console /// </summary> public: void Print() { Console::WriteLine("MyXmlAttributeData::FireWireType = {0}", FireWireType); for(int i=0; i < SupportedDevices->Length ;i++) { Console::WriteLine("MyXmlAttributeData::SupportedDevices[{0}] = {1}", i, SupportedDevices[i]); } } };
/// <summary> /// This is the class which contains the support routines. /// </summary> public ref class XmlSerializeSample { /// <summary> /// Displays usage information /// </summary> public: static void usage() { Console::WriteLine("executable_file_name [/serialize | /deserialize]"); Console::WriteLine(" [/simple | /attribute | /override] [/file file.xml]"); Console::WriteLine("Available options:"); Console::WriteLine(" /serialize Serialize data to XML"); Console::WriteLine(" /deserialize Deserialize data from XML"); Console::WriteLine(" /simple Perform simple XML serialization"); Console::WriteLine(" /attribute Perform XML serialization with attributes"); Console::WriteLine(" /override Perform XML serialization with overridden attributes"); Console::WriteLine(" /file filename File name to serialize to or deserialize from"); Console::WriteLine(); }
};
/// <summary> /// Main routine that parses the command line, creates the select file stream, and /// serializes or deserializes the indicated class. /// </summary> /// <param name="args">Command line parameters passed to application</param> int main(array<System::String ^> ^args) { SerializeOperation serializeOp = SerializeOperation::opWrite; SerializeClass serializeClass = SerializeClass::classSimple; // A default file name String^ fileName = "simpletest.xml";
// Parse the command line if(args->Length != 0) { for (int i = 0; i < args->Length; i++) { if (String::Compare(args[i], "/serialize", true) == 0) { serializeOp = SerializeOperation::opWrite; } else if (String::Compare(args[i], "/deserialize", true) == 0) { serializeOp = SerializeOperation::opRead; } else if (String::Compare(args[i], "/file", true) == 0) { try { fileName = args[++i]; } catch (IndexOutOfRangeException^) { Console::WriteLine("Please specify output filename!\n"); XmlSerializeSample::usage(); } } else if (String::Compare(args[i], "/simple", true) == 0) { serializeClass = SerializeClass::classSimple; } else if (String::Compare(args[i], "/attribute", true) == 0) { serializeClass = SerializeClass::classAttribute; } else if (String::Compare(args[i], "/override", true) == 0) { serializeClass = SerializeClass::classOverride; } else { XmlSerializeSample:: usage(); return 0; } } } else { XmlSerializeSample:: usage(); return 0; }
FileStream^ fileStream = nullptr; XmlSerializer^ xmlSerializer = nullptr;
try { // Create an instance of the FileStream on which serialization or deserialization will take place. if (serializeOp == SerializeOperation::opWrite) { String^ s = "hello"; array<Byte>^ data = Text::Encoding::ASCII->GetBytes(s->ToCharArray()); fileStream = gcnew FileStream( fileName, FileMode::Create, FileAccess::Write, FileShare::None ); fileStream->Write(data, 0, 0); Console::WriteLine("Writing is OK, filename is " + fileName); } else if (serializeOp == SerializeOperation::opRead) { fileStream = gcnew FileStream( fileName, FileMode::Open, FileAccess::Read, FileShare::Read ); Console::WriteLine("Reading is OK..."); }
if (serializeOp == SerializeOperation::opWrite) { // If serialization (writing) is selected, we need to create an instance // of the class which will be serialized to the stream. if (serializeClass == SerializeClass::classSimple) { // Create an instance of the simple class MyXmlSimpleData^ simpleData = nullptr; simpleData = gcnew MyXmlSimpleData(); xmlSerializer = gcnew XmlSerializer(MyXmlSimpleData::typeid);
try { Console::WriteLine("Serializing a simple class..."); xmlSerializer->Serialize(fileStream, simpleData); } catch (InvalidOperationException^ err) { Console::WriteLine("Serialization of simple class failed: {0}", err->Message); } } else if (serializeClass == SerializeClass::classAttribute) { // Create an instance of the class containing XML attributes MyXmlAttributeData^ attributeData = nullptr; attributeData = gcnew MyXmlAttributeData(); xmlSerializer = gcnew XmlSerializer(MyXmlAttributeData::typeid);
try { Console::WriteLine("Serializing an attributed class..."); xmlSerializer->Serialize(fileStream, attributeData); } catch (InvalidOperationException^ err) { Console::WriteLine("Serialization of attributed class failed: {0}", err->Message); } } else if (serializeClass == SerializeClass::classOverride) { // Create an instance of the class which has its own override method // and call that method to serialize the data. MyXmlSimpleData^ simpleData = nullptr; simpleData = gcnew MyXmlSimpleData(); simpleData->OverrideSerialization(fileStream); } } else if (serializeOp == SerializeOperation::opRead) { // If deserialization (reading) is selected, simply create an instance // of the XmlSerializer for the type being serialized and call the deserialize method. if (serializeClass == SerializeClass::classSimple) { MyXmlSimpleData^ simpleData = nullptr; xmlSerializer = gcnew XmlSerializer(MyXmlSimpleData::typeid);
try { Console::WriteLine("De-serializing a simple class..."); simpleData = (MyXmlSimpleData^)xmlSerializer->Deserialize(fileStream); simpleData->Print(); } catch (InvalidOperationException^ err) { Console::WriteLine("Deserialization of simple class failed: {0}", err->Message); } } else if ((serializeClass == SerializeClass::classAttribute) || (serializeClass == SerializeClass::classOverride)) { MyXmlAttributeData^ attributeData = nullptr; xmlSerializer = gcnew XmlSerializer(MyXmlAttributeData::typeid);
try { Console::WriteLine("De-serializing an overriding/attributed class..."); attributeData = (MyXmlAttributeData^)xmlSerializer->Deserialize(fileStream); attributeData->Print(); } catch (InvalidOperationException^ err) { Console::WriteLine("Deserialization of overriding/attributed class failed: {0}", err->Message); } } } } catch(Exception^ err) { Console::WriteLine("Error lor! -->" + err->Message); }
// Close up try { Console::WriteLine("Closing FileStream..."); fileStream->Close(); } catch (Exception^ err) { Console::WriteLine("Error closing FileStream..." + err->Message); } return 0; } |
Build and run the project.
The following are the output samples when the program run at the command prompt.
![]() |
|
Well, we have some error as seen in the above screenshot when de-serializing the overriding and the details is shown below. It is left for your exercise and please take sometime to solve it.
Unhandled Exception: System.InvalidOperationException: There is an error in XML document (2, 2). ---> System.InvalidOperationException: <MyXmlSimpleData xmlns=''> was not expected. at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderMyXmlAttributeData.Read3_MyXmlAttributeData() --- End of inner exception stack trace --- at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) at System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle) at System.Xml.Serialization.XmlSerializer.Deserialize(Stream stream) at main(String[ ] args) at mainCRTStartupStrArray(String[ ] arguments) |
The following is the generated XML file opened in the Internet browser.