Thursday 18 May 2017

Serialization 03 xml

Xml serialization accomplishes at a high level the same thing as binary serialization, the main difference being that unlike binary serialization it's human readable however that comes at the price of size. Xml serialization is more robust and thus takes up a larger memory footprint. The implementations however similar do have some slight differences.
  • Classes must be public
  • Classes must have parameterless constructors
  • Properties must have public setters
  • [Nonserializable] attribute does nothing
  • Base classes need to have the XmlInclude attribute with their superclass types
//for xml serialization class must be public
//base classes need to have teh XmlInclude attribute
//with any superclass types that inherit from them
[XmlInclude(typeof(Employee))]
[Serializable]
public class Person
{
    //for xml serialization, all properties must have public setters
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public Person() { }
    public Person(string FirstName, string LastName)
    {
        this.FirstName = FirstName;
        this.LastName = LastName;
    }

    public string FullName { get { return $"{FirstName} {LastName}"; } }
}

[Serializable]
public class Employee : Person
{
    static int _runningId;
    public int Id { getset; } = _runningId++;
    public string Wage { get; set; } = null;
    public Employee() { }
    public Employee(string FirstName, string LastName) : base(FirstName, LastName) { }
}

now as you can see the marking classes as xml serialize-able is exactly the same as it was for binary serialization. as for the actual serialization and de-serialization.

class Program
{
    static void Main(string[] args)
    {
        var ppl = new Person[] { new Person("John", "Smith"), new Person("Jane","Doe"),
        new Employee("Sally","Johnson"),new Person("Alejandro", "Cruz"),
        new Person("Diago","Pendaz"), new Employee("Tim","Chan")};

        var xmlSerializer = new XmlSerializer(ppl.GetType());

        using (var fs = new FileStream("c:/ppl.xml", FileMode.Create, FileAccess.Write))
            xmlSerializer.Serialize(fs, ppl);


        Person[] People;
        using (var fs = new FileStream("c:/ppl.xml", FileMode.Open, FileAccess.Read))
            People = xmlSerializer.Deserialize(fs) as Person[];

        foreach (var p in People)
            Console.WriteLine(p.FullName);
    }
}

again almost an identical procedure, other then when defining you're XmlSerializer you have to specify the type.

now the result of our efforts is

<?xml version="1.0"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Person>
    <FirstName>John</FirstName>
    <LastName>Smith</LastName>
  </Person>
  <Person>
    <FirstName>Jane</FirstName>
    <LastName>Doe</LastName>
  </Person>
  <Person xsi:type="Employee">
    <FirstName>Sally</FirstName>
    <LastName>Johnson</LastName>
    <Id>0</Id>
  </Person>
  <Person>
    <FirstName>Alejandro</FirstName>
    <LastName>Cruz</LastName>
  </Person>
  <Person>
    <FirstName>Diago</FirstName>
    <LastName>Pendaz</LastName>
  </Person>
  <Person xsi:type="Employee">
    <FirstName>Tim</FirstName>
    <LastName>Chan</LastName>
    <Id>1</Id>
  </Person>

</ArrayOfPerson>

pretty neat, but i don't want the FirstName and LastName properties to be nodes of my person element, i want them to be attributes, and actually i don't want to even store the Id numbers of my employees.

//for xml serializtion class must be public
//base classes need to have teh XmlInclude attribute
//with any superclass types that inherit from them
[XmlInclude(typeof(Employee))]
[Serializable]
public class Person
{
    //for xml serialization all properties must have public setters
    [XmlAttribute]
    public string FirstName { get; set; }
    [XmlAttribute]
    public string LastName { get; set; }
    public Person() { }
    public Person(string FirstName, string LastName)
    {
        this.FirstName = FirstName;
        this.LastName = LastName;
    }

    public string FullName { get { return $"{FirstName} {LastName}"; } }
}

[Serializable]
public class Employee : Person
{
    static int _runningId;
    [XmlIgnore]
    public int Id { get; set; } = _runningId++;
    public string Wage { get; set; } = null;
    public Employee() { }
    public Employee(string FirstName, string LastName) : base(FirstName, LastName) { }

}

well all i had to do is mark my properties with [XmlAttribute] attributes or [XmlIgnore] to modify my xml to what i had in mind

<?xml version="1.0"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Person FirstName="John" LastName="Smith" />
  <Person FirstName="Jane" LastName="Doe" />
  <Person xsi:type="Employee" FirstName="Sally" LastName="Johnson" />
  <Person FirstName="Alejandro" LastName="Cruz" />
  <Person FirstName="Diago" LastName="Pendaz" />
  <Person xsi:type="Employee" FirstName="Tim" LastName="Chan" />

</ArrayOfPerson>

Pretty straight forward, there's numerous XmlAttributes to leverage

Now as the binaryformater had the ISerializable interface to intercept the serialization process the xmlSeraializer has the IXmlSerialisable interface. now this can be used to encrypt your data, but it could also be leveraged for just about any other manipulation you'd want of your data.