Сериализация XML и схема без xsd.exe - PullRequest
5 голосов
/ 03 декабря 2008

Я использую сериализацию XML для чтения моих Config-POCO.

Чтобы получить поддержку intellisense в Visual Studio для файлов XML, мне нужен файл схемы. Я могу создать схему с помощью xsd.exe mylibrary.dll, и это прекрасно работает.

Но я хочу, чтобы схема всегда создавалась, если я сериализовал объект в файловую систему. Есть ли способ без использования xsd.exe?

Ответы [ 4 ]

11 голосов
/ 03 декабря 2008

спасибо, это был правильный путь для меня. Решение:

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();
11 голосов
/ 23 октября 2009

Решение, опубликованное Уиллом выше, работало замечательно, за исключением того, что я понял, что сгенерированная схема не отражает атрибуты разных членов класса. Например, класс, украшенный атрибутами подсказки сериализации (см. Образец ниже), не будет отображаться правильно.

    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; }

    }

Для решения этой проблемы я создал несколько вспомогательных функций, которые используют отражение для обхода иерархии классов, чтения атрибутов и заполнения объекта XmlAttributeOverrides, который можно передать в 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();
        }
    }

Надеюсь, это поможет кому-то еще.

3 голосов
/ 03 декабря 2008

Посмотрите на класс System.Xml.Serialization.XmlSchemaExporter. Я не могу вспомнить точные детали, но в этом пространстве имен достаточно функциональности, чтобы выполнить то, что вам нужно.

0 голосов
/ 08 августа 2017

Улучшение версии Мэтта Мюррела: рекурсивное применение XmlAttributes для типа пользователя с вложенным свойством (например, свойства CustomerClass).

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

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

    foreach (var prop in t.GetProperties())
    {
        var propType = prop.PropertyType;
        if (propType.IsGenericType) // is list?
        {
            var args = propType.GetGenericArguments();
            if (args != null && args.Length == 1)
            {                        
                var genType = args[0];
                if (genType.Name.ToLower() != "object")
                {
                    var list2 = GetAttributeList(prop.GetCustomAttributes(false));
                    xao.Add(t, prop.Name, list2);
                    AttachXmlAttributes(xao, all, genType);
                }                        
            }
        }
        else
        {
            var list2 = GetAttributeList(prop.GetCustomAttributes(false));
            xao.Add(t, prop.Name, list2);
            AttachXmlAttributes(xao, all, prop.PropertyType);
        }
    }
}        

private static XmlAttributes GetAttributeList(object[] attributes)
{
    var list = new XmlAttributes();
    foreach (var attr in attributes)
    {
        Type type = attr.GetType();
        switch (type.Name)
        {
            case "XmlAttributeAttribute":
                list.XmlAttribute = (XmlAttributeAttribute)attr;
                break;                    
            case "XmlRootAttribute":
                list.XmlRoot = (XmlRootAttribute)attr;
                break;
            case "XmlElementAttribute":
                list.XmlElements.Add((XmlElementAttribute)attr);
                break;
            case "XmlArrayAttribute":
                list.XmlArray = (XmlArrayAttribute)attr;
                break;
            case "XmlArrayItemAttribute":
                list.XmlArrayItems.Add((XmlArrayItemAttribute)attr);
                break;
        }
    }
    return list;
}
...