If you need to serialize your business objects into XML you may also need to generate automatically XML Schema so you can check the XML against that schema before try to deserialize it. This is one utility that will do that for you. Now, let’s first see one utility that will do basic XML serialization and deserialization objects.

public static class SerializationUtil
{
    private static readonly Dictionary<Type, XmlSerializer> _serializersCache = 
                            new Dictionary<Type, XmlSerializer>();

    private static XmlSerializer GetSerializer(Type type)
    {
        XmlSerializer xs;
        if (!_serializersCache.TryGetValue(type, out xs))
        {
            xs = new XmlSerializer(type);
            _serializersCache[type] = xs;
        }
        return xs;
    }

    public static string ToXml<T>(T obj)
    {
        var xs = GetSerializer(typeof(T));
        var sb = new StringBuilder();
        using (var writer = new StringWriter(sb))
        {
            xs.Serialize(writer, obj);
            writer.Close();
        }
        return sb.ToString();
    }
    public static T FromXml<T>(string xml)
    {
        T a;
        var xs = GetSerializer(typeof(T));
        using (var reader = new StringReader(xml))
        {
            a = (T)xs.Deserialize(reader);
            reader.Close();
        }
        return a;
    }
}

You can use it as:

// serialize business object into xml string
var xmlString = SerializationUtil.ToXml<MyObj>(obj);
// deserialize xml string into business object
var obj = SerializationUtil.FromXml<MyObj>(xmlString);

Now, to generate XML schema from the interface your object implements you can use this utility:

public static class InterfaceToXsdUtil
{
    private const string XMLSchema = "http://www.w3.org/2001/XMLSchema";
    private const string XS = "xs";
    
    private static bool IsFloat(Type type)
    {
        return (type == typeof (Int64) ||
                type == typeof (UInt64) ||
                type == typeof (Single) ||
                type == typeof (Double) ||
                type == typeof (Decimal));
    }
    private static bool IsInt(Type type)
    {
        return (type == typeof(Byte) ||
                type == typeof(SByte) ||
                type == typeof(Int16) ||
                type == typeof(UInt16) ||
                type == typeof(Int32) ||
                type == typeof(UInt32));
    }

    private static void WriteType(XmlWriter xtw, string name, 
                                  Type type, int depth, 
                                  Func<XmlWriter, string, Type, bool> customProc)
    {
        if (customProc != null && customProc(xtw, name, type))
        {
            return;
        }

        xtw.WriteStartElement(XS, "element", XMLSchema);
        { // attributes
            xtw.WriteAttributeString("name", name);
            if (depth > 0)
            {
                xtw.WriteAttributeString("minOccurs", "0");
                xtw.WriteAttributeString("maxOccurs", "1");
            }
        
            xtw.WriteStartElement(XS, "complexType", XMLSchema);
            {
                xtw.WriteStartElement(XS, "sequence", XMLSchema);
                {
                    foreach (var pi in type.GetProperties())
                    {
                        var propType = pi.PropertyType;
                        if (propType.IsClass && propType != typeof(string))
                        {
                            WriteType(xtw, pi.Name, pi.PropertyType, depth + 1, customProc);
                        }
                        else
                        {
                            xtw.WriteStartElement(XS, "element", XMLSchema);
                            { // attributes
                                xtw.WriteAttributeString("name", pi.Name);
                                if (propType.IsValueType)
                                {
                                    xtw.WriteAttributeString("minOccurs", "1");
                                }
                                else
                                {
                                    xtw.WriteAttributeString("minOccurs", "0");
                                }

                                if (IsFloat(propType))
                                {
                                    xtw.WriteAttributeString("type", "xs:decimal");
                                }
                                else if (IsInt(propType))
                                {
                                    xtw.WriteAttributeString("type", "xs:integer");
                                }
                                else if (propType == typeof(bool))
                                {
                                    xtw.WriteAttributeString("type", "xs:boolean");
                                }
                                else if (propType == typeof(DateTime))
                                {
                                    xtw.WriteAttributeString("type", "xs:date");
                                }
                                else // if(propType == typeof(string))
                                {
                                    xtw.WriteAttributeString("type", "xs:string");
                                }
                            }
                            xtw.WriteEndElement();
                        }
                    }
                }
                xtw.WriteEndElement();
            }
            xtw.WriteEndElement();
        }
        xtw.WriteEndElement();
    }

    public static string ToXsd<T>()
    {
        var type = typeof(T);
        var root = type.Name.StartsWith("I") ? type.Name.Substring(1) : type.Name;
        return ToXsd<T>(root, null);
    }
    
    public static string ToXsd<T>(string root)
    {
        return ToXsd<T>(root, null);
    }

    public static string ToXsd<T>(string root, 
                    Func<XmlWriter, string, Type, bool> customProc)
    {
        var type = typeof (T);
        var sb = new StringBuilder();
        using (var xtw = new XmlTextWriter(new StringWriter(sb)))
        {
            xtw.Formatting = Formatting.Indented;
            xtw.WriteStartElement(XS, "schema", XMLSchema);
            { // attributes
                xtw.WriteAttributeString("id", root);
                xtw.WriteAttributeString("xmlns", "");
                xtw.WriteAttributeString("xmlns:msdata", 
                                "urn:schemas-microsoft-com:xml-msdata");
            }
            WriteType(xtw, root, type, 0, customProc);
            xtw.WriteEndElement();
        }
        return sb.ToString();
    }

}

The way you use it is as follow:

var xsdString = InterfaceToXsdUtil.ToXsd<IMyBusinessObject>();

Share this post:   digg     Stumble Upon     del.icio.us     E-mail

Michal
Posted on 8/5/2009 10:34:11 AM

I believe that GetSerializer method should be corrected to:
private static XmlSerializer GetSerializer(Type type)
{
XmlSerializer xs;
if (!_serializersCache.TryGetValue(type, out xs))
{
xs = new XmlSerializer(type);
_serializersCache[type] = xs;
}
return xs;
}
so that newly created serializer is stored somewhere.
Great article besides that :)

AngryScotch
Posted on 8/5/2009 11:00:57 AM

What is the meaning of StringWriterWithEncoding class? Personally I don’t believe in magic, so I don’t know what that class is supposed to do. Are you certified by MS, therefore you can cast spells and throw exceptions? ;-)

Vladimir Bodurov
Posted on 8/9/2009 8:36:15 AM

Hi Michal,

Sorry for the late response, I was on a vacation in the wild with no access to the Internet.

You are right, I missed that part of the caching algorithm. I will correct it. Thank you.

Vladimir Bodurov
Posted on 8/9/2009 8:50:17 AM

Hi Scotch,

This is the way to change the encoding of the XML header in the XmlWriter but because in the XML schema we don't use XML header it can safely be omitted. I will make that change to keep it simple.

Commenting temporarily disabled