September 20, 2012

Code Repository - XML Serialization Helper

XmlSerializationHelper.cs
//------------------------------------------------------------------------------
// Created by: Pete
// Created on: Sep 19, 2012
//------------------------------------------------------------------------------
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using AdventureWorks.Infrastructure.Exceptions;

namespace AdventureWorks.Infrastructure
{
    /// <summary>
    /// A UTF-8 XML serialization helper supporting serialization and deserialization without BOM
    /// </summary>
    public static class XmlSerializationHelper
    {
        /// <summary>
        /// To serialize an object
        /// </summary>
        /// <param name="value">the object to be serialized</param>
        /// <returns>An XML string</returns>
        /// <exception cref="XmlSerializationHelperException">Thrown when the object to be serialized is null or the object cannot be serialized.</exception>
        public static string Serialize(object value)
        {
            if (value == null)
            {
                throw new XmlSerializationHelperException("The object to be serialized is null");
            }           

            try
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    XmlWriterSettings settings = new XmlWriterSettings();
                    settings.Encoding = new UTF8Encoding(false);
                    XmlWriter writer = XmlWriter.Create(ms, settings);
                    XmlSerializer xs = new XmlSerializer(value.GetType());
                    xs.Serialize(writer, value);
                    return Encoding.UTF8.GetString(ms.ToArray());
                }
            }
            catch (Exception ex)
            {
                throw new XmlSerializationHelperException("Cannot serialize the object", ex);
            }
        }

        /// <summary>
        /// To deserialize an XML string
        /// </summary>
        /// <typeparam name="T">The object type to which will be deserialized</typeparam>
        /// <param name="xml">The XML string for deserialization</param>
        /// <returns>An object</returns>
        /// <exception cref="XmlSerializationHelperException">Thrown when the XML string is empty/null or the XML string cannot be deserialized.</exception>
        public static T Deserialize<T>(string xml)
        {
            if (string.IsNullOrEmpty(xml))
            {
                throw new XmlSerializationHelperException("the XML string is null or empty");
            }

            try
            {
                XmlSerializer xs = new XmlSerializer(typeof(T));
                byte[] xmlToBytes = Encoding.UTF8.GetBytes(xml);
                using (MemoryStream ms = new MemoryStream(xmlToBytes))
                {
                    return (T)xs.Deserialize(ms);;
                }
            }
            catch (Exception ex)
            {
                throw new XmlSerializationHelperException("Cannot get the deserialized object", ex);
            }
        }
    }
}
XmlSerializationHelperException.cs
//------------------------------------------------------------------------------
// Created by: Pete
// Created on: Sep 19, 2012
//------------------------------------------------------------------------------

using System;
using System.Runtime.Serialization;

namespace AdventureWorks.Infrastructure.Exceptions
{
    /// <summary>
    /// The exception for the XmlSerializationHelper class
    /// </summary>
    [Serializable]
    public class XmlSerializationHelperException : Exception, ISerializable
    {
        public XmlSerializationHelperException()
            : base()
        {
        }

        public XmlSerializationHelperException(string message)
            : base(message)
        {
        }

        public XmlSerializationHelperException(string message, Exception innerException)
            : base(message, innerException)
        {
        }

        protected XmlSerializationHelperException(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
        }
    }
}

Unit Test
//------------------------------------------------------------------------------
// Created by: Pete
// Created on: Sep 19, 2012
//------------------------------------------------------------------------------
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
using AdventureWorks.Infrastructure.Exceptions;
using NUnit.Framework;

namespace AdventureWorks.Infrastructure.Tests.UnitTest
{
    /// <summary>
    /// A unit test to test the XmlSerializationHelper class
    /// </summary>
    [TestFixture]
    public class XmlSerializationHelperTests
    {
        public class User
        {
            public string Name { get; set; }
            public DateTime Birth { get; set; }
        }

        [Test]
        public void Serialize_ExistingObject_ReturnSerializedString()
        {
            User user = new User() { Name = "Pete Chen", Birth = new DateTime(1982, 8, 20) };
            string result = XmlSerializationHelper.Serialize(user);

            XDocument xd = XDocument.Parse(result);

            Assert.AreEqual("utf-8", xd.Declaration.Encoding.ToLower(), "The encoding is not UTF-8");
            Assert.AreEqual("User", xd.Root.Name.LocalName, "The root element name is not User");
            Assert.AreEqual("Name", xd.Root.Elements().First().Name.LocalName, "The name of the first child element is not Name");
            Assert.AreEqual("Birth", xd.Root.Elements().Last().Name.LocalName, "The name of the last child element is not Birth");
        }

        [Test]
        public void Serialize_ExistingObject_ReturnNonBomedSerializedString()
        {
            User user = new User() { Name = "Pete Chen", Birth = new DateTime(1982, 8, 20) };
            string xml = XmlSerializationHelper.Serialize(user);

            using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)))
            {
                byte[] xmlToBytes = ms.ToArray();

                Assert.AreNotEqual(0xef, xmlToBytes[0]);
                Assert.AreNotEqual(0xbb, xmlToBytes[0]);
                Assert.AreNotEqual(0xbf, xmlToBytes[0]);
            }            
        }

        [ExpectedException(typeof(XmlSerializationHelperException), ExpectedMessage = "The object to be serialized is null")]
        [Test]
        public void Serialize_NullObject_ThrowException()
        {
            string xml = XmlSerializationHelper.Serialize(null);
        }

        [ExpectedException(typeof(XmlSerializationHelperException), ExpectedMessage = "Cannot serialize the object")]
        [Test]
        public void Serialize_AnonymousObject_ThrowException()
        {
            string xml = XmlSerializationHelper.Serialize(new { Name = "Pete Chen" });
        }

        [Test]
        public void Deserialize_ValidXmlString_ReturnDeserializedObject()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-8""?><User xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema""><Name>Pete Chen</Name><Birth>1982-08-20T00:00:00</Birth></User>";

            User user = XmlSerializationHelper.Deserialize<User>(xml);

            Assert.IsInstanceOf<User>(user, "Deserialized object is not User");
            Assert.AreEqual("Pete Chen", user.Name, "Name is not Pete Chen");
            Assert.AreEqual(new DateTime(1982, 8, 20), user.Birth, "Birth is not 1982.08.20");
        }

        [Test]
        public void Deserialize_BomedXmlString_ReturnDeserializedObject()
        {
            string xml = this.GetBomedXmlString();
            User user = XmlSerializationHelper.Deserialize<User>(xml);

            Assert.IsInstanceOf<User>(user, "Deserialized object is not User");
            Assert.AreEqual("Pete Chen", user.Name, "Name is not Pete Chen");
            Assert.AreEqual(new DateTime(1982, 8, 20), user.Birth, "Birth is not 1982.08.20");
        }

        [ExpectedException(typeof(XmlSerializationHelperException), ExpectedMessage = "Cannot get the deserialized object")]
        [Test]
        public void Deserialize_InvalidXmlString_ThrowException()
        {
            string xml = @"<?xml version=""1.0"" encoding=""utf-8""?><User1 xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema""><Name>Pete Chen</Name><Birth>1982-08-20T00:00:00</Birth></User1>";
            User user = XmlSerializationHelper.Deserialize<User>(xml);
        }

        [ExpectedException(typeof(XmlSerializationHelperException), ExpectedMessage = "the XML string is null or empty")]
        [Test]
        public void Deserialize_EmptyXmlString_ThrowException()
        {
            string xml = string.Empty;
            User user = XmlSerializationHelper.Deserialize<User>(xml);
        }

        private string GetBomedXmlString()
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Encoding = new UTF8Encoding(true);

            using (MemoryStream ms = new MemoryStream())
            {
                XmlWriter writer = XmlWriter.Create(ms, settings);
                XmlSerializer xs = new XmlSerializer(typeof(User));

                User testUser = new User() { Name = "Pete Chen", Birth = new DateTime(1982, 8, 20) };
                xs.Serialize(writer, testUser);

                return Encoding.UTF8.GetString(ms.ToArray());
            }
        }
    }
}

No comments: