views:

1101

answers:

3

i use xml-serialization for the reading of my Config-POCOs.

to get intellisense support in visual studio for xml files i need a schema file. i can create the schema with xsd.exe mylibrary.dll , this works fine.

but i want that the schema is always created if i serialize an object to the filesystem. is there any way without using xsd.exe?

+1  A: 

Look at the System.Xml.Serialization.XmlSchemaExporter class. I cant recall the exact details, but there is enough functionality in that namespace to do what you require.

leppie
+5  A: 

thank you, this was the right way for me. solution:

XmlReflectionImporter importer = new XmlReflectionImporter();
XmlSchemas schemas = new XmlSchemas();
XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
Type type = toSerialize.GetType();
XmlTypeMapping map = importer.ImportTypeMapping(type);
exporter.ExportTypeMapping(map);

TextWriter tw = new StreamWriter(fileName + ".xsd");
schemas[0].Write(tw);
tw.Close();
Now that I see you code, it 'looks' what I did way back (just cant recall where!)
leppie
+1  A: 

The solution posted above by Will worked wonderfully, except I realized that the schema generated did not reflect the attributes on the different class members. For example a class decorated with serialization hint attributes (see the sample below), would have not rendered correctly.

    public class Test
    {
        [XmlAttribute()]
        public string Attribute { get; set; }
        public string Description { get; set; }

        [XmlArray(ElementName = "Customers")]
        [XmlArrayItem(ElementName = "Customer")]
        public List<CustomerClass> blah { get; set; }

    }

To address this, I created a few helper functions that use reflection to traverse the class hierarchy, read the attributes, and populate a XmlAttributeOverrides object that can be passed into the XmlReflectionImporter.

    public static void AttachXmlAttributes(XmlAttributeOverrides xao, Type t)
    {
        List<Type> types = new List<Type>();
        AttachXmlAttributes(xao, types, t);
    }

    public static void AttachXmlAttributes(XmlAttributeOverrides xao, List<Type> all, Type t)
    {
        if(all.Contains(t))
            return;
        else
            all.Add(t);

        XmlAttributes list1 = GetAttributeList(t.GetCustomAttributes(false));
        xao.Add(t, list1);

        foreach (var prop in t.GetProperties())
        {
            XmlAttributes list2 = GetAttributeList(prop.GetCustomAttributes(false));
            xao.Add(t, prop.Name, list2);
            AttachXmlAttributes(xao, all, prop.PropertyType);
        }
    }

    private static XmlAttributes GetAttributeList(object[] attributes)
    {
        XmlAttributes list = new XmlAttributes();
        foreach (var attribute in attributes)
        {
            Type type = attribute.GetType();
            if (type.Name == "XmlAttributeAttribute") list.XmlAttribute = (XmlAttributeAttribute)attribute;
            else if (type.Name == "XmlArrayAttribute") list.XmlArray = (XmlArrayAttribute)attribute;
            else if (type.Name == "XmlArrayItemAttribute") list.XmlArrayItems.Add((XmlArrayItemAttribute)attribute);

        }
        return list;
    }
    public static string GetSchema<T>()
    {
        XmlAttributeOverrides xao = new XmlAttributeOverrides();
        AttachXmlAttributes(xao, typeof(T));

        XmlReflectionImporter importer = new XmlReflectionImporter(xao);
        XmlSchemas schemas = new XmlSchemas();
        XmlSchemaExporter exporter = new XmlSchemaExporter(schemas);
        XmlTypeMapping map = importer.ImportTypeMapping(typeof(T));
        exporter.ExportTypeMapping(map);

        using (MemoryStream ms = new MemoryStream())
        {
            schemas[0].Write(ms);
            ms.Position = 0;
            return new StreamReader(ms).ReadToEnd();
        }
    }

Hope this helps someone else.

Matt Murrell