XmlSerializableBase Class

This class was designed as a wrapper for XML schema-generated classes ( eg. via xsd.exe or XSDObjectGen ).


public abstract class XmlSerializableBase : IXmlSerializable
{

    #region Contract
    
        //an XSD-generated class
        protected abstract object DataObject
        {
            get;
            set;
        }
        
        //necessary because the DataObject may be null,
        //so using 'typeof' with XmlSerializer wouldn't work
        public abstract Type DataObjectType
        {
            get;
        }

    #endregion

    #region Copy-Constructor

        protected XmlSerializableBase( XmlSerializableBase original )
        {
            using ( MemoryStream stream = new MemoryStream() )
            {
                original.WriteXml( stream );

                stream.Seek( 0, SeekOrigin.Begin );

                this.ReadXml( stream );
            }
        }

    #endregion

    #region ToString() Override

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            StringWriter writer = new StringWriter( sb );

            this.WriteXml( writer );

            return sb.ToString();
        }

    #endregion

    #region IXmlSerializable Members

        public virtual void WriteXml( XmlWriter writer )
        {
            XmlSerializer serializer;

            serializer = new XmlSerializer( this.DataObjectType );

            serializer.Serialize( writer, this.DataObject );
        }

        public virtual void ReadXml( XmlReader reader )
        {
            XmlSerializer serializer;

            serializer = new XmlSerializer( this.DataObjectType );

            this.DataObject = serializer.Deserialize( reader );
        }

        public virtual XmlSchema GetSchema()
        {
            return null;
        }

    #endregion

    #region Additional Read/Write Xml

        public virtual void ReadXml( string filename )
        {
            using (StreamReader reader = new StreamReader( filename )) 
            {
                this.ReadXml( reader );
            }
        }
        
        public virtual void ReadXml( TextReader reader )
        {
            XmlSerializer serializer;

            serializer = new XmlSerializer( this.DataObjectType );

            this.DataObject = serializer.Deserialize( reader );
        }
        
        
        public virtual void ReadXml( Stream stream )
        {
            XmlSerializer serializer;

            serializer = new XmlSerializer( this.DataObjectType );

            this.DataObject = serializer.Deserialize( stream );
        }
        
        public virtual void WriteXml( Stream stream )
        {
            XmlSerializer serializer;

            serializer = new XmlSerializer( this.DataObjectType  );

            serializer.Serialize( stream, this.DataObject);
        }

        public virtual void WriteXml( TextWriter writer )
        {
            XmlSerializer serializer;

            serializer = new XmlSerializer( this.DataObjectType );

            serializer.Serialize( writer, this.DataObject );
        }
    
        public virtual void WriteXml( string filename )
        {
            using (StreamWriter writer = new StreamWriter( filename )) 
            {
                this.WriteXml( writer );
            }
        }

    #endregion
    
    #region Read/Write Encrypted

    public virtual void WriteEncryptedXml( string filename, 
                                            ICryptoTransform transform )
    {
        CryptoStream cryptstream;
        FileStream outfile;

        outfile = new FileStream( filename,
                                    FileMode.OpenOrCreate,
                                    FileAccess.Write );

        cryptstream = new CryptoStream( outfile,
                                        transform,
                                        CryptoStreamMode.Write );

        this.WriteXml( cryptstream );

        cryptstream.Close();

        outfile.Close();
    }

    public virtual void WriteEncryptedXml( string filename, 
                                            byte[] rijnKey,
                                            byte[] rijnIV )
    {
        SymmetricAlgorithm rijndael;
        ICryptoTransform transform;
        
        rijndael = SymmetricAlgorithm.Create();

        transform = rijndael.CreateEncryptor( rijnKey, rijnIV );

        this.WriteEncryptedXml( filename, transform );
    }

    public virtual void ReadEncryptedXml( string filename,
                                            ICryptoTransform transform )
    {
        CryptoStream cryptstream;
        FileStream infile;

        infile = new FileStream( filename, 
                                   FileMode.Open,
                                   FileAccess.Read );

        cryptstream = new CryptoStream( infile,
                                    transform,
                                    CryptoStreamMode.Read );

        this.ReadXml( cryptstream );

        cryptstream.Close();

        infile.Close();
    }

    public virtual void ReadEncryptedXml( string filename,
                                            byte[] rijnKey,
                                            byte[] rijnIV )
    {
        SymmetricAlgorithm rijndael;
        ICryptoTransform transform;
        
        rijndael = SymmetricAlgorithm.Create();

        transform = rijndael.CreateDecryptor( rijnKey, rijnIV );

        this.ReadEncryptedXml( filename, transform );
    }

    #endregion
}


[TestFixture]
public class TestXmlSerializableBase
{
    
    Contact contact;

    public TestXmlSerializableBase()
    {
        contact = new Contact();
        contact.FirstName = "Jimmy";
        contact.LastName = "Joe";
        contact.Address.BuildingNumber = "222";
        contact.Address.StreetName = "HighLow Street";
        contact.Address.AdditionalAddressLines.Add( "Piccadilly" );
        contact.Address.AdditionalAddressLines.Add( "Leicester Square" );

        Debug.Write( "xml: " + contact.ToString() );
    }

    [Test]
    public void TestToStringMethod()
    {
        XmlDocument doc;
        XmlNode node;

        doc = new XmlDocument();

        doc.LoadXml( contact.ToString() );

        node = doc.SelectSingleNode( "/Contact/FirstName" );

        Assert.IsTrue( contact.FirstName == node.InnerText );

        node = doc.SelectSingleNode( "/Contact/LastName" );

        Assert.IsTrue( contact.LastName == node.InnerText );
    }
    
    [Test]
    public void TestCloneMethod()
    {
        Contact copy = contact.Clone();

        Assert.IsNotNull( copy );

        Assert.IsTrue( contact.FirstName == copy.FirstName );

        copy.FirstName = "Billy";

        Assert.IsFalse( contact.FirstName == copy.FirstName );
    }
    
    [Test]
    public void TestReadWriteEncryptedXml()
    {
        byte[] key;
        byte[] iv;
        SymmetricAlgorithm rijndael;
        string filename;
        Contact decryptedContact;

        rijndael = SymmetricAlgorithm.Create();

        rijndael.GenerateIV();

        rijndael.GenerateKey();

        key = rijndael.Key;

        iv = rijndael.IV;

        filename = Path.GetTempFileName();

        this.contact.WriteEncryptedXml( filename, key, iv );

        Assert.IsTrue( File.Exists( filename ) );

        //now try to 'rehydrate' the encrypted file
        decryptedContact = new Contact();

        decryptedContact.ReadEncryptedXml( filename, key, iv );

        Assert.IsTrue( contact.FirstName == decryptedContact.FirstName );

        Assert.IsTrue( contact.LastName == decryptedContact.LastName );
    }
}

public class Contact : XmlSerializableBase
{
    private ContactData __contactData = new ContactData();

    public Contact() {}

    protected Contact( Contact original ) : base(original) {}

    protected override object DataObject
    {
        get { return __contactData; }
        set {__contactData = (ContactData) value; }
    }

    public override Type DataObjectType
    {
        get {return typeof( ContactData ); }
    }

    public string FirstName
    {
        get { return __contactData.FirstName; }
        set { __contactData.FirstName = value; }
    }

    public string LastName
    {
        get { return __contactData.LastName; }
        set { __contactData.LastName = value; }
    }

    public AddressDataType Address
    {
        get { return __contactData.Address; }
    }

    public Contact Clone()
    {
        return new Contact( this );
    }

}

[XmlRoot("Contact"), Serializable]
public class ContactData
{
    public ContactData() {}

    [XmlElement( "FirstName", typeof( string ) )]
    public string FirstName;

    [XmlElement( "LastName", typeof( string ) )]
    public string LastName;

    [XmlElement( "Address", typeof( AddressDataType ) )]
    public AddressDataType Address = new AddressDataType();
}

[XmlType("AddressDataType"),Serializable]
public class AddressDataType
{
    public AddressDataType() {}
    
    [XmlElement( "BuildingNumber", typeof( string ) )]
    public string BuildingNumber;

    [XmlElement( "StreetName", typeof( string ) )]
    public string StreetName;

    [XmlElement( "AdditionalAddressLine", typeof( string ) )]
    public ArrayList AdditionalAddressLines = new ArrayList();

    [XmlElement( "District", typeof( string ) )]
    public string District;

    [XmlElement( "PostalTown", typeof( string ) )]
    public string PostalTown;

    [XmlElement( "PostalCode", typeof( string ) )]
    public string PostalCode;

    [XmlElement( "Region", typeof( string ) )]
    public string Region;

    [XmlElement( "CountrySubEntity", typeof( string ) )]
    public string CountrySubEntity;

    [XmlElement( "Country", typeof( string ) )]
    public string Country;
}