11

In .Net 4 or 4.5, how would you design a serializable class that contains an instance of one class from a set of classes? For instance, suppose I have a Garage class, which can hold an instance of any "vehicle" type classes, say Car, Boat, Motorcycle, Motorhome. But the Garage can only hold an instance of one of those classes. I have tried a few different ways of doing this, but my problem is to make it serializable.

Here is a starting example where there is only one option for the instance in the Garage class. You should be able to plug it right into a new console app and try it.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace Patterns
{
    [Serializable()]
    public class Garage
    {
        private Vehicle _MyVehicle;

        public Garage()
        {
        }
        public string GarageOwner { get; set; }
        public Vehicle MyVehicle
        {
            get { return _MyVehicle; }
            set { _MyVehicle = value; }
        }
    }

    [Serializable()]
    public class Vehicle
    {
        public string VehicleType { get; set; }
        public int VehicleNumber { get; set; }
    }

    class Serializer
    {
        static string _StartupPath = @"C:\Projects\Patterns\Data\";
        static string _StartupFile = "SerializerTest.xml";
        static string _StartupXML = _StartupPath + _StartupFile;

        static void Main(string[] args)
        {
            Console.Write("Press w for write. Press r for read:");
            ConsoleKeyInfo cki = Console.ReadKey(true);
            Console.WriteLine("Pressed: " + cki.KeyChar.ToString());
            if (cki.KeyChar.ToString() == "w")
            {
                Garage MyGarage = new Garage();
                MyGarage.GarageOwner = "John";
                MyGarage.MyVehicle = new Vehicle();
                MyGarage.MyVehicle.VehicleType = "Car";
                MyGarage.MyVehicle.VehicleNumber = 1234;
                WriteGarageXML(MyGarage);
                Console.WriteLine("Serialized");
            }
            else if (cki.KeyChar.ToString() == "r")
            {
                Garage MyGarage = ReadGarageXML();
                Console.WriteLine("Deserialized Garage owned by " +  MyGarage.GarageOwner);
            }
            Console.ReadKey();
        }
        public static void WriteGarageXML(Garage pInstance)
        {
            XmlSerializer writer = new XmlSerializer(typeof(Garage));
            using (FileStream file = File.OpenWrite(_StartupXML))
            {
                writer.Serialize(file, pInstance);
            }
        }
        public static Garage ReadGarageXML()
        {
            XmlSerializer reader = new XmlSerializer(typeof(Garage));
            using (FileStream input = File.OpenRead(_StartupXML))
            {
                return reader.Deserialize(input) as Garage;
            }
        }
    }
}
John
  • 239
  • 1
  • 3
  • 8

2 Answers2

6

Based on another SO article, this is what finally worked for me. It can serialize and deserialize cleanly. Using this example, I can design a "tree" of objects which have options for what is used. So this could be extended that a Car can have one Engine of several different Engine type classes and one Interior of several different Interior types... and so on.

The code began to work by adding the statements below like: [XmlInclude(typeof(Car))]

But please let me know if there are better ways!

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace Patterns
{
    public class Garage
    {
        private Vehicle _MyVehicle;

        public Garage()
        {
        }
        public string GarageOwner { get; set; }

        public Vehicle MyVehicle
        {
            get { return _MyVehicle; }
            set { _MyVehicle = value; }
        }
    }

    [XmlInclude(typeof(Car))]
    [XmlInclude(typeof(Boat))]
    [XmlInclude(typeof(Motorcycle))]
    [XmlInclude(typeof(Motorhome))]
    public abstract class Vehicle
    {
        public string VehicleType { get; set; }
        public int VehicleNumber { get; set; }
    }
    public class Car : Vehicle
    {
        public int Doors { get; set; }
    }
    public class Boat : Vehicle
    {
        public int Engines { get; set; }
    }
    public class Motorcycle : Vehicle
    {
        public int Wheels { get; set; }
    }
    public class Motorhome : Vehicle
    {
        public int Length { get; set; }
    }

    class Serializer
    {
        static string _StartupPath = @"C:\Projects\Patterns\Data\";
        static string _StartupFile = "SerializerTest.xml";
        static string _StartupXML = _StartupPath + _StartupFile;

        static void Main(string[] args)
        {
            Console.Write("Press w for write. Press r for read:");
            ConsoleKeyInfo cki = Console.ReadKey(true);
            Console.WriteLine("Pressed: " + cki.KeyChar.ToString());
            if (cki.KeyChar.ToString() == "w")
            {
                Garage MyGarage = new Garage();
                MyGarage.GarageOwner = "John";
                Car c = new Car();
                c.VehicleType = "Lexus";
                c.VehicleNumber = 1234;
                c.Doors = 4;
                MyGarage.MyVehicle = c;
                WriteGarageXML(MyGarage);
                Console.WriteLine("Serialized");
            }
            else if (cki.KeyChar.ToString() == "r")
            {
                Garage MyGarage = ReadGarageXML();
                Console.WriteLine("Deserialized Garage owned by " + MyGarage.GarageOwner);
            }
            Console.ReadKey();
        }
        public static void WriteGarageXML(Garage pInstance)
        {
            XmlSerializer writer = new XmlSerializer(typeof(Garage));
            using (FileStream file = File.OpenWrite(_StartupXML))
            {
                writer.Serialize(file, pInstance);
            }
        }
        public static Garage ReadGarageXML()
        {
            XmlSerializer reader = new XmlSerializer(typeof(Garage));
            using (FileStream input = File.OpenRead(_StartupXML))
            {
                return reader.Deserialize(input) as Garage;
            }
        }
    }    
}
Community
  • 1
  • 1
John
  • 239
  • 1
  • 3
  • 8
4

To serialize sequences of serializable classes you can use Generic list instances.

I generated this

<?xml version="1.0"?>
<Garage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <GarageOwner>John</GarageOwner>
  <MyVehicles>
    <Vehicle>
      <VehicleType>Car</VehicleType>
      <VehicleNumber>1234</VehicleNumber>
    </Vehicle>
    <Vehicle>
      <VehicleType>Boat</VehicleType>
      <VehicleNumber>56234</VehicleNumber>
    </Vehicle>
  </MyVehicles>
</Garage>

By simply converting the MyVehicle to a generic list

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace Patterns
{
    [Serializable()]
    public class Garage
    {
        public string GarageOwner { get; set; }
        public List<Vehicle> MyVehicles { get; set; }
    }

    [Serializable()]
    public class Vehicle
    {
        public string VehicleType { get; set; }
        public int VehicleNumber { get; set; }
    }

    class Serializer
    {
        static string _StartupPath = @"C:\temp\";
        static string _StartupFile = "SerializerTest.xml";
        static string _StartupXML = _StartupPath + _StartupFile;

        static void Main(string[] args)
        {
            Console.Write("Press w for write. Press r for read:");
            ConsoleKeyInfo cki = Console.ReadKey(true);
            Console.WriteLine("Pressed: " + cki.KeyChar.ToString());
            if (cki.KeyChar.ToString() == "w")
            {
                Garage MyGarage = new Garage();
                MyGarage.GarageOwner = "John";

                // Create some vehicles
                var myVehicle1 = new Vehicle();
                myVehicle1.VehicleType = "Car";
                myVehicle1.VehicleNumber = 1234;

                var myVehicle2 = new Vehicle();
                myVehicle2.VehicleType = "Boat";
                myVehicle2.VehicleNumber = 56234;

                // Create a new instance and add the vehicles
                MyGarage.MyVehicles = new List<Vehicle>()
                {
                    myVehicle1, 
                    myVehicle2
                };

                WriteGarageXML(MyGarage);
                Console.WriteLine("Serialized");
            }
            else if (cki.KeyChar.ToString() == "r")
            {
                Garage MyGarage = ReadGarageXML();
                Console.WriteLine("Deserialized Garage owned by " +  MyGarage.GarageOwner);
            }
            Console.ReadKey();
        }
        public static void WriteGarageXML(Garage pInstance)
        {
            XmlSerializer writer = new XmlSerializer(typeof(Garage));
            using (FileStream file = File.OpenWrite(_StartupXML))
            {
                writer.Serialize(file, pInstance);
            }
        }
        public static Garage ReadGarageXML()
        {
            XmlSerializer reader = new XmlSerializer(typeof(Garage));
            using (FileStream input = File.OpenRead(_StartupXML))
            {
                return reader.Deserialize(input) as Garage;
            }
        }
    }
}
Eric Herlitz
  • 25,354
  • 27
  • 113
  • 157