从 C# 类代码生成 xsd 注释和文档标签

Posted

技术标签:

【中文标题】从 C# 类代码生成 xsd 注释和文档标签【英文标题】:Generate xsd annotation and documentation tags from C# class code 【发布时间】:2017-08-29 02:02:43 【问题描述】:

使用 xds.exe(或 other methods)从类生成 XSD 文件效果很好,但我找不到将文档(或任何类型的描述)插入输出 XSD 的方法。

例如C#类

public class Animal

    public int NumberOfLegs;

生成 XSD

<?xml version="1.0" encoding="utf-16"?>
<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Animal" nillable="true" type="Animal" />
  <xs:complexType name="Animal">
    <xs:sequence>
      <xs:element minOccurs="1" maxOccurs="1" name="NumberOfLegs" type="xs:int" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>

不过,我希望能够将 XSD 注释作为元数据添加到类中,以便 XSD 出现

<xs:complexType name="Animal">
  <xs:sequence>
    <xs:element minOccurs="1" maxOccurs="1" name="NumberOfLegs" type="xs:int">
      <xs:annotation>
        <xs:documentation>Will need to be greater than 0 to walk!</xs:documentation>
      </xs:annotation>
    </xs:element>
  </xs:sequence>
</xs:complexType>

是否有任何简洁的方法可以在 C# 代码中实现这一点?向 xml 元素/属性添加任何类型的描述的任何方式都可以。注释必须与实际代码类似:

public class Animal

    [XmlAnnotation("Will need to be greater than 0 to walk!")]
    public int NumberOfLegs;

也就是说,它需要从 cmets 自动记录。

【问题讨论】:

【参考方案1】:

尝试以下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Xml;
using System.Xml.Linq;
using System.IO;


namespace ConsoleApplication49


    class Program
    
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        
            StreamReader reader = new StreamReader(FILENAME);
            reader.ReadLine(); //skip the xml identification with utf-16 encoding
            XDocument doc = XDocument.Load(reader);

            XElement firstNode = (XElement)doc.FirstNode;
            XNamespace nsXs = firstNode.GetNamespaceOfPrefix("xs");

            XElement sequence = doc.Descendants(nsXs + "element").FirstOrDefault();

            sequence.Add(new XElement(nsXs + "annotation",
                new XElement(nsXs + "documention", "Will need to be greater than 0 to walk!")
                ));

        
    



【讨论】:

抱歉,我的问题可能不够具体。 cmets 需要与实际代码并列,我将更新问题。【参考方案2】:

这是一个非常丑陋的解决方案,但它适用于我的项目。

  public virtual XElement GetSchema() 
        var schemas = new XmlSchemas();
        var exporter = new XmlSchemaExporter(schemas);
        var importer = new XmlReflectionImporter();
        var mapping = importer.ImportTypeMapping(this.GetType());
        exporter.ExportTypeMapping(mapping);

        using (var schemaWriter = new StringWriter()) 
            foreach (System.Xml.Schema.XmlSchema schema in schemas) 
                schema.Write(schemaWriter);
            

            var xsdText = schemaWriter.ToString();
            var xsd = XElement.Parse(xsdText);
            using (var controller = new XsdAnnotationController()) 
              xsd = controller.AddAnnotations(xsd);
            

            return xsd;
        
    

注解属性

[AttributeUsage(AttributeTargets.Class)]
public class XmlAnnotationAttribute : Attribute 
    public string Annotation  get; set; 
    public XmlAnnotationAttribute()  
    public XmlAnnotationAttribute(string annotation) : this() 
        Annotation = annotation;
    

注解控制器

public class XsdAnnotationController : IDisposable 
    List<Type> Types = null;
    public XsdAnnotationController() 
        var asm = System.Reflection.Assembly.GetCallingAssembly();
        Type[] allTypes = null;
        try 
            allTypes = asm.GetTypes();
        
        catch (Reflection.ReflectionTypeLoadException ex) 
            allTypes = ex.Types;
        
        if (allTypes != null) 
            Types = allTypes.Where(t => t.IsClass).ToList();
        
    
    public XElement AddAnnotations(XElement xsd) 
        if (xsd != null && xsd.HasElements) 

            // Add annotations for classes
            var xsdTypes = xsd.Elements().Where(x => (x.Name.LocalName == "complexType" || x.Name.LocalName == "simpleType") && x.Attribute("name") != null && x.Attribute("name").Value != null);
            foreach (var xsdType in xsdTypes) 
                var typeName = xsdType.Attribute("name").Value;
                var type = GetClassType(typeName);
                var annotation = GetTypeAnnotation(type);
                if (annotation != null && xsdType.Elements().Where(x => x.Name.LocalName == "annotation").Count() == 0) 
                    xsdType.AddFirst(new XElement(XName.Get("annotation", xsdType.Name.NamespaceName),
                        new XElement(XName.Get("documentation", xsdType.Name.NamespaceName),
                            new XText(annotation)
                        )
                    ));

                    var elements = xsd.Descendants().Where(x => x.Attribute("type") != null && (x.Attribute("type").Value == typeName || x.Attribute("type").Value.EndsWith($":typeName")));
                    foreach (var element in elements) 
                        if (element.Elements().Where(x => x.Name.LocalName == "annotation").Count() == 0) 
                            element.AddFirst(new XElement(XName.Get("annotation", element.Name.NamespaceName),
                                new XElement(XName.Get("documentation", element.Name.NamespaceName),
                                    new XText(annotation)
                                )
                            ));
                        
                    
                

                // Add annotations for properties
                if (type != null) 
                    var xsdElements = xsdType.Descendants().Where(x => x.Name.LocalName == "element" && x.Attribute("name") != null);
                    var properties = type.GetProperties();
                    foreach (var property in properties) 
                        var propertyName = GetPropertyXmlName(property);
                        var propertyAnnotation = GetPropertyAnnotation(property);
                        if (propertyAnnotation != null) 
                            var xsdElement = xsdElements.Where(x => x.Attribute("name").Value == propertyName).FirstOrDefault();
                            if (xsdElement.IsNotNull()) 
                                if (xsdElement.Elements().Where(x => x.Name.LocalName == "annotation").Count() == 0) 
                                    xsdElement.AddFirst(new XElement(XName.Get("annotation", xsdElement.Name.NamespaceName),
                                        new XElement(XName.Get("documentation", xsdElement.Name.NamespaceName),
                                            new XText(propertyAnnotation)
                                        )
                                    ));
                                
                            
                        
                    
                

            

        
        return xsd;
    

    public void Dispose()  

    private Type GetClassType(string xsdTypeName) 
        if (Types != null && xsdTypeName != null) 
            foreach (var type in Types) 
                var xmlTypeName = GetXmlTypeName(type);
                if (xmlTypeName != null && xmlTypeName.Equals(xsdTypeName)) 
                    return type;
                
            
        
        return default;
    

    private string GetTypeAnnotation(Type type) 
        if (type != null) 
            XmlAnnotationAttribute[] attributes = (XmlAnnotationAttribute[])type.GetCustomAttributes(typeof(XmlAnnotationAttribute), false);
            if (attributes != null && attributes.Length > 0) 
                return attributes[0].Annotation;
            
        
        return default;
    

    private string GetPropertyAnnotation(Reflection.PropertyInfo property) 
        if (property != null) 
            XmlAnnotationAttribute[] attributes = (XmlAnnotationAttribute[])property.GetCustomAttributes(typeof(XmlAnnotationAttribute), false);
            if (attributes != null && attributes.Length > 0) 
                return attributes[0].Annotation;
            
        
        return default;
    

    private string GetPropertyXmlName(Reflection.PropertyInfo property) 
        if (property.IsNotNull()) 
            XmlElementAttribute[] attributes = (XmlElementAttribute[])property.GetCustomAttributes(typeof(XmlElementAttribute), false);
            if (attributes != null && attributes.Length > 0) 
                return attributes[0].ElementName;
            
            else 
                XmlAttributeAttribute[] attributes2 = (XmlAttributeAttribute[])property.GetCustomAttributes(typeof(XmlAttributeAttribute), false);
                if (attributes2 != null && attributes2.Length > 0) 
                    return attributes2[0].AttributeName;
                
            

            return property.Name;
        
        return default;
    

    public string GetXmlTypeName(Type type) 
        if (type.IsNotNull()) 
            XmlTypeAttribute[] attributes = (XmlTypeAttribute[])type.GetCustomAttributes(typeof(XmlTypeAttribute), false);
            if (attributes != null && attributes.Length > 0) 
                return attributes[0].TypeName;
            
            else 
                return type.Name;
            
        
        return default;
    

现在你可以描述类了

// [XmlType("Animal")]
[XmlAnnotation("Animal class annotation")]
public class Animal  
  [XmlAnnotation("Will need to be greater than 0 to walk!")] 
  public int NumberOfLegs  get; set; 

【讨论】:

使用示例:github.com/abcpro-pl/elektronizacja-prawa/blob/master/nes/v2.0/…github.com/abcpro-pl/elektronizacja-prawa/tree/master/nes/v2.0/…

以上是关于从 C# 类代码生成 xsd 注释和文档标签的主要内容,如果未能解决你的问题,请参考以下文章

是否有用于注释 C# 代码的标准(如 phpdoc 或 python 的文档字符串)?

从 XSD 生成 PHP 类?

无法从 XSD 文件创建 c# 类

不使用注释的 Java 代码到 XML/XSD

为 C# 生成的 XML 文档的 XSD? [关闭]

向依赖于XSD中的信息的JAXB生成的类添加注释