博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
XsdGen:通过自定义Attribute与反射自动生成XSD
阅读量:5025 次
发布时间:2019-06-12

本文共 17763 字,大约阅读时间需要 59 分钟。

前言

        系统之间的数据交互往往需要事先定义一些契约,在WCF中我们需要先编写XSD文件,然后通过自动代码生成工具自动生成C#对象。对于刚刚接触契约的人来说,掌握XMLSpy之类的软件之后确实比手写XML效率要高,但还是有些学习成本的。此外XML的tag太多,如果设计的类型属性过多,手写XSD也不太现实,很难专注于设计。

        于是我想能不能先用C#写好类型,然后自动生成标准格式的XSD呢。经过三天左右的设计和实现,目前实现了以下功能:

1. 支持Class和Enum类型的设计

2. 支持基元类型、自定义类型、泛型列表、自定义类型数组等属性

3. 支持自定义类型之间的依赖关系

4. 支持契约分组(指定Request/Response分到同一个xsd文件)

5. 支持契约汇总(对于自定义类型,最终体现在一个汇总xsd文件中,并自动引用其它xsd文件)

        开源地址:。我刚接触SOA不久,目前只添加了xsd的minOccurs、maxOccurs等基本属性,对于其它属性并没有全部集成进去,集成方式也非常简单,在Attribute中添加相应的属性,并在XsdBuilder.cs中添加相应的处理。

效果

        先看一下实现的效果,例如我定义了以下类型:FoodOrderRequest、FoodOrderResponse、PayType

using System;using System.Collections.Generic;using XsdAttribute;[assembly: XsdSchema(    TargetNamespace = "http://xx.com/framework/soa/sample/v1",    XmlNamespace = "http://xx.com/framework/soa/sample/v1",    Namespace = "http://xx.com/framework/soa/sample/v1",    Common = "http://xx.com/common/types/v1")][assembly: XsdImport(Id = "SOACommonTypes",    Namespace = "http://xx.com/common/types/v1",    SchemaLocation = "SOACommonTypes_V1.0.0.xsd")]namespace TestDLL{    [XsdComplexType(Annotation = "订餐申请", FileGroup = "FoodOrder")]    public class FoodOrderRequest    {        [XsdElement(MinOccurs = "1", Annotation = "餐馆编号")]        public int RestaurantId { get; set; }        [XsdElement(MinOccurs = "1", Annotation = "餐馆名称")]        public string RestaurantName { get; set; }        [XsdElement(Annotation = "订餐日期")]        public DateTime OrderDate { get; set; }        [XsdElement(MinOccurs = "0", MaxOccurs = "unbounded", Annotation = "食品编号")]        public List
FoodId { get; set; } [XsdElement(MinOccurs = "1", Annotation = "业务类型")] public PayType BusinessType { get; set; } } [XsdComplexType(Annotation = "订餐结果", FileGroup = "FoodOrder")] public class FoodOrderResponse { [XsdElement(MinOccurs = "1", Annotation = "订单编号")] public int OrderId { get; set; } [XsdElement(Annotation = "预计送达时间")] public DateTime DeliveryTime { get; set; } } [XsdSimpleType(Annotation = "付款类型", FileGroup = "PayType")] public enum PayType { 现金, 支付宝, 微信, 网银 }}

        工具自动为我生成了3个文件:

FoodOrder.xsd

订餐申请
餐馆编号
餐馆名称
订餐日期
食品编号
业务类型
订餐结果
订单编号
预计送达时间

PayType.xsd

TestDLL.xsd

自定义Attribute

1. Assembly的Attribute

[AttributeUsage(AttributeTargets.Assembly)]public class XsdSchema : Attribute{    public string TargetNamespace { get; set; }    public string XmlNamespace { get; set; }    public string Namespace { get; set; }    public string Common { get; set; }    //汇总dll中的类型,生成总的xsd    private string _packageId;    ///     /// 生成XSD的文件名称    ///     public string PackageId    {        get { return _packageId; }        set        {            //去除文件名中非法字符            var regex = new Regex(@"\:|\;|\/|\\|\||\,|\*|\?|\""|\<|\>");            _packageId = regex.Replace(value, "");        }    }    public static XsdSchema Get(Assembly assembly)    {        return (XsdSchema) GetCustomAttribute(assembly, typeof (XsdSchema));    }}

        前几个是一些命名空间的定义,可以根据自己公司的业务规则进行设计。默认情况下,汇总文件的targetId、文件名称以及生成的文件夹名称都是PackageId决定,如果dll没有指定该值,默认则是dll的Name。

        除了定义Schema,每个xsd文件中可能还需要导入一些默认的公共类型,在这种情况下,可以为assembly添加如下Attribute:

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]public class XsdImport : Attribute{    public string Id { get; set; }    public string SchemaLocation { get; set; }    public string Namespace { get; set; }    public static IEnumerable
Get(Assembly assembly) { var attributes = GetCustomAttributes(assembly, typeof (XsdImport)); return attributes.Cast
(); }}

2. Class类型的Attribute

[AttributeUsage((AttributeTargets.Class))]public class XsdComplexType : Attribute{    private string _fileGroup;    ///     /// 生成XSD的文件名称    ///     public string FileGroup    {        get { return _fileGroup; }        set        {            //去除文件名中非法字符            var regex = new Regex(@"\:|\;|\/|\\|\||\,|\*|\?|\""|\<|\>");            _fileGroup = regex.Replace(value, "");        }    }    public string Annotation { get; set; }    public static XsdComplexType Get(Type type)    {        return (XsdComplexType) GetCustomAttribute(type, typeof (XsdComplexType));    }}

        其中必须指定FileGroup值,相同的FileGroup值,最后会生成到同一个xsd文件中。

2. Property的Attribute

[AttributeUsage((AttributeTargets.Property))]public class XsdElement : Attribute{    public string MinOccurs { get; set; }    public string MaxOccurs { get; set; }    public string Annotation { get; set; }    public static XsdElement Get(PropertyInfo propertyInfo)    {        return (XsdElement) GetCustomAttribute(propertyInfo, typeof (XsdElement));    }}

        如果自定义类型的属性是List或者Array,要将MaxOccurs设为大于0的数或者unbounded。

3. Enum类型的Attribute

[AttributeUsage((AttributeTargets.Enum))]public class XsdSimpleType : Attribute{    private string _fileGroup;    ///     /// 生成XSD的文件名称    ///     public string FileGroup    {        get { return _fileGroup; }        set        {            //去除文件名中非法字符            var regex = new Regex(@"\:|\;|\/|\\|\||\,|\*|\?|\""|\<|\>");            _fileGroup = regex.Replace(value, "");        }    }    public string Annotation { get; set; }    public static XsdSimpleType Get(Type type)    {        return (XsdSimpleType) GetCustomAttribute(type, typeof (XsdSimpleType));    }}

        与XsdComplexType类似,相同的FileGroup类型,最后都归到同一个xsd文件中。

反射DLL并生成XSD

1. XsdFile结构定义

public class XsdFile{    public XsdFile()    {        Imports = new List
(); Elements = new List
(); } public XElement Schema { get; set; } ///
///
Assembly级别的Import
///
自定义复合对象的Import
///
public List
Imports { get; set; } ///
///
自定义Class类型
///
自定义枚举类型
///
public List
Elements { get; set; } public XElement ToXML() { foreach (var import in Imports) { Schema.Add(import); } foreach (var element in Elements) { Schema.Add(element); } return Schema; }}

        基本思路是Schema添加所有的Import和Element,最后生成XML。

2. XsdBuilder

using System;using System.Collections.Generic;using System.IO;using System.Linq;using System.Reflection;using System.Xml.Linq;using XsdAttribute;namespace XsdGen{    public class XsdBuilder    {        private readonly XNamespace _xs = "http://www.w3.org/2001/XMLSchema";        private Assembly _assembly;        //Key:FileGroup, Value:对应的XsdFile        private Dictionary
_xsdFiles; //自定义Class对象的声明,用于生成汇总xsd private List
_elements; public void Build(Assembly assembly) { _assembly = assembly; _xsdFiles = new Dictionary
(); _elements = new List
(); XElement[] defaultImports = GetDefaultImports(_assembly).ToArray(); XsdSchema defaultSchema = XsdSchema.Get(_assembly); string directoryName = defaultSchema.PackageId ?? _assembly.GetName().Name; if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } BuildTypes(_assembly); foreach (var item in _xsdFiles) { item.Value.Schema = GetSchema(defaultSchema, item.Key); foreach (var import in defaultImports) { item.Value.Imports.Add(import); } //生成XSD文件 string fileName = string.Format("{0}/{1}.xsd", directoryName, item.Key); item.Value.ToXML().Save(fileName); Console.WriteLine("Generate {0}", fileName); } //生成汇总XSD文件 var summaryXsdFile = BuildSummary(defaultSchema, defaultImports, directoryName); string summaryXsdFileName = string.Format("{0}/{1}.xsd", directoryName, directoryName); summaryXsdFile.ToXML().Save(summaryXsdFileName); Console.WriteLine("{0}Generate Summary{0}\n{1}", new String('=', 10), summaryXsdFileName); } private XElement GetSchema(XsdSchema xsdSchema, string id) { var schema = new XElement( _xs + "schema", new XAttribute("id", id), new XAttribute("targetNamespace", xsdSchema.TargetNamespace), new XAttribute("elementFormDefault", "qualified"), new XAttribute("attributeFormDefault", "unqualified"), new XAttribute(XNamespace.Xmlns + "xs", _xs.ToString()) ); if (!string.IsNullOrEmpty(xsdSchema.XmlNamespace)) schema.SetAttributeValue("xmlns", xsdSchema.XmlNamespace); if (!string.IsNullOrEmpty(xsdSchema.Namespace)) schema.SetAttributeValue(XNamespace.Xmlns + "ns", xsdSchema.Namespace); if (!string.IsNullOrEmpty(xsdSchema.Common)) schema.SetAttributeValue(XNamespace.Xmlns + "common", xsdSchema.Common); return schema; } private IEnumerable
GetDefaultImports(Assembly assembly) { var xsdImports = XsdImport.Get(assembly); return xsdImports.Select(xsdImport => new XElement( _xs + "import", new XAttribute("id", xsdImport.Id), new XAttribute("schemaLocation", xsdImport.SchemaLocation), new XAttribute("namespace", xsdImport.Namespace) )); } private void BuildTypes(Assembly assembly) { var types = assembly.GetTypes(); foreach (var type in types) { string fileGroup; if (type.IsClass) { var element = BuildElement(type); _elements.Add(element); //_xsdFiles[fileGroup].Elements.Add(element); var complexTypeElement = BuildComplexType(type, out fileGroup); _xsdFiles[fileGroup].Elements.Add(complexTypeElement); } else if (type.IsEnum) { var simpleTypeElement = BuildSimpleType(type, out fileGroup); _xsdFiles[fileGroup].Elements.Add(simpleTypeElement); } } } public XElement BuildElement(Type type) { //只有Request或者Response对象类型,末尾自动添加Type string name = (type.Name.EndsWith("Request") || type.Name.EndsWith("Response")) ? type.Name + "Type" : type.Name; return new XElement( _xs + "element", new XAttribute("name", type.Name), new XAttribute("nillable", true), new XAttribute("type", "ns:" + name) ); } private XElement BuildComplexType(Type type, out string fileGroup) { var xsdComplexType = XsdComplexType.Get(type); //添加XSD文件 fileGroup = xsdComplexType.FileGroup; SetDefaultFile(fileGroup); //只有Request或者Response对象类型,末尾自动添加Type string name = (type.Name.EndsWith("Request") || type.Name.EndsWith("Response")) ? type.Name + "Type" : type.Name; var complexTypeElement = new XElement( _xs + "complexType", new XAttribute("name", name) ); if (!string.IsNullOrEmpty(xsdComplexType.Annotation)) { complexTypeElement.Add(new XElement( _xs + "annotation", new XElement(_xs + "documentation", xsdComplexType.Annotation) )); } var sequenceElement = BuildSequence(type); AddProperties(type, sequenceElement); complexTypeElement.Add(sequenceElement); return complexTypeElement; } private XElement BuildSequence(Type type) { var sequence = new XElement(_xs + "sequence");return sequence; } private XsdFile BuildSummary(XsdSchema defaultSchema, XElement[] defaultImports, string packageId) { XsdFile xsdFile = new XsdFile(); xsdFile.Schema = GetSchema(defaultSchema, packageId); foreach (var import in defaultImports) { xsdFile.Imports.Add(import); } //include所有其它自动生成的xsd文件 foreach (var item in _xsdFiles) { xsdFile.Imports.Add(new XElement( _xs + "include", new XAttribute("schemaLocation", item.Key + ".xsd") )); } //添加dll中所有定义的element foreach (var item in _elements) { xsdFile.Elements.Add(item); } return xsdFile; } private void AddProperties(Type type, XElement sequenceElement) { var properties = type.GetProperties(); foreach (var propertyInfo in properties) { var typeName = Common.GetXsdTypeName(propertyInfo.PropertyType); var propertyElement = new XElement( _xs + "element", new XAttribute("name", propertyInfo.Name), new XAttribute("type", typeName) ); var xsdElement = XsdElement.Get(propertyInfo); if (xsdElement != null) { if (!string.IsNullOrEmpty(xsdElement.MinOccurs)) propertyElement.SetAttributeValue("minOccurs", xsdElement.MinOccurs); if (!string.IsNullOrEmpty(xsdElement.MaxOccurs)) propertyElement.SetAttributeValue("maxOccurs", xsdElement.MaxOccurs); if (!string.IsNullOrEmpty(xsdElement.Annotation)) propertyElement.Add(new XElement( _xs + "annotation", new XElement( _xs + "documentation", xsdElement.Annotation ) )); } //判断是否自定义类型, 添加Import if (!typeName.StartsWith("xs:")) { var parentClassFileGroup = XsdComplexType.Get(type).FileGroup; var propertyClassFileGroup = Common.GetFileGroup(propertyInfo.PropertyType); if (parentClassFileGroup != propertyClassFileGroup) { string importXsd = propertyClassFileGroup + ".xsd"; //判断是否已经存在该Import if (_xsdFiles[parentClassFileGroup].Imports.All(item => item.Attribute("schemaLocation").Value != importXsd)) { _xsdFiles[parentClassFileGroup].Imports.Add( new XElement( _xs + "include", new XAttribute("schemaLocation", importXsd) ) ); } } } sequenceElement.Add(propertyElement); } } private XElement BuildSimpleType(Type type, out string fileGroup) { var xsdSimpleType = XsdSimpleType.Get(type); //添加XSD文件 fileGroup = xsdSimpleType.FileGroup; SetDefaultFile(fileGroup); var simpleTypeElement = new XElement( _xs + "simpleType", new XAttribute("name", type.Name), new XAttribute("final", "restriction") ); var restrictionElement = new XElement( _xs + "restriction", new XAttribute("base", "xs:string") ); foreach (var val in Enum.GetNames(type)) { restrictionElement.Add( new XElement( _xs + "enumeration", new XAttribute("value", val) ) ); } simpleTypeElement.Add(restrictionElement); return simpleTypeElement; } private void SetDefaultFile(string fileGroup) { if (!_xsdFiles.ContainsKey(fileGroup)) { var xsdFile = new XsdFile(); _xsdFiles[fileGroup] = xsdFile; } } }}

        XsdBuilder依次反射出Assembly、Class、Enum、Property的自定义Attribute,根据XSD的规则构造XElement。主要步骤在Builder函数中都有所体现。

应用

        现在写接口契约就很爽了,只要定义好类生成DLL,通过XsdGen.exe就可轻松生成接口契约微笑

 

参考

转载于:https://www.cnblogs.com/technology/p/XsdGen.html

你可能感兴趣的文章
JAVA修饰符类型(public,protected,private,friendly)
查看>>
IOS开发之Bug--使用KVC的易错情况
查看>>
python list和tuple
查看>>
基础薄弱的反思
查看>>
代码说明call和apply方法的区别 (咱们这方面讲解的少,这样的题有变式,需要举例讲解一下)...
查看>>
T-SQL 类型转换
查看>>
在eclipse中设计BPMN 2.0工作流定义的根本步骤
查看>>
Json对象与Json字符串互转(4种转换方式)
查看>>
PAT甲级1002 链表实现方法
查看>>
查看Linux信息
查看>>
Python中sys模块sys.argv取值并判断
查看>>
【详记MySql问题大全集】四、设置MySql大小写敏感(踩坑血泪史)
查看>>
并查集
查看>>
ubuntu 11.04下android开发环境的搭建!
查看>>
Bzoj 3343: 教主的魔法
查看>>
括号序列(栈)
查看>>
一件趣事
查看>>
DevExpress控件TExtLookupComboBox实现多列模糊匹配输入的方法
查看>>
atom 调用g++编译cpp文件
查看>>
H3C HDLC协议特点
查看>>