首页 > 学院 > 开发设计 > 正文

XsdGen:通过自定义Attribute与反射自动生成XSD

2019-11-17 02:25:46
字体:
来源:转载
供稿:网友

XsdGen:通过自定义Attribute与反射自动生成XSD

前言

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

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

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

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

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

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

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

开源地址:https://github.com/CreateChen/XsdGen。我刚接触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<int> 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

<?xml version="1.0" encoding="utf-8"?><xs:schema id="FoodOrder" targetNamespace="http://xx.com/framework/soa/sample/v1" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://xx.com/framework/soa/sample/v1" xmlns:ns="http://xx.com/framework/soa/sample/v1" xmlns:common="http://xx.com/common/types/v1">  <xs:include schemaLocation="PayType.xsd" />  <xs:import id="SOACommonTypes" schemaLocation="SOACommonTypes_V1.0.0.xsd" namespace="http://xx.com/common/types/v1" />  <xs:complexType name="FoodOrderRequestType">    <xs:annotation>      <xs:documentation>订餐申请</xs:documentation>    </xs:annotation>    <xs:sequence>      <xs:element name="RestaurantId" type="xs:int" minOccurs="1">        <xs:annotation>          <xs:documentation>餐馆编号</xs:documentation>        </xs:annotation>      </xs:element>      <xs:element name="RestaurantName" type="xs:string" minOccurs="1">        <xs:annotation>          <xs:documentation>餐馆名称</xs:documentation>        </xs:annotation>      </xs:element>      <xs:element name="OrderDate" type="xs:dateTime">        <xs:annotation>          <xs:documentation>订餐日期</xs:documentation>        </xs:annotation>      </xs:element>      <xs:element name="FoodId" type="xs:int" minOccurs="0" maxOccurs="unbounded">        <xs:annotation>          <xs:documentation>食品编号</xs:documentation>        </xs:annotation>      </xs:element>      <xs:element name="BusinessType" type="PayType" minOccurs="1">        <xs:annotation>          <xs:documentation>业务类型</xs:documentation>        </xs:annotation>      </xs:element>    </xs:sequence>  </xs:complexType>  <xs:complexType name="FoodOrderResponseType">    <xs:annotation>      <xs:documentation>订餐结果</xs:documentation>    </xs:annotation>    <xs:sequence>      <xs:element name="OrderId" type="xs:int" minOccurs="1">        <xs:annotation>          <xs:documentation>订单编号</xs:documentation>        </xs:annotation>      </xs:element>      <xs:element name="DeliveryTime" type="xs:dateTime">        <xs:annotation>          <xs:documentation>预计送达时间</xs:documentation>        </xs:annotation>      </xs:element>    </xs:sequence>  </xs:complexType></xs:schema>

PayType.xsd

<?xml version="1.0" encoding="utf-8"?><xs:schema id="PayType" targetNamespace="http://xx.com/framework/soa/sample/v1" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://xx.com/framework/soa/sample/v1" xmlns:ns="http://xx.com/framework/soa/sample/v1" xmlns:common="http://xx.com/common/types/v1">  <xs:import id="SOACommonTypes" schemaLocation="SOACommonTypes_V1.0.0.xsd" namespace="http://xx.com/common/types/v1" />  <xs:simpleType name="PayType" final="restriction">    <xs:restriction base="xs:string">      <xs:enumeration value="现金" />      <xs:enumeration value="支付宝" />      <xs:enumeration value="微信" />      <xs:enumeration value="网银" />    </xs:restriction>  </xs:simpleType></xs:schema>

TestDLL.xsd

<?xml version="1.0" encoding="utf-8"?><xs:schema id="TestDLL" targetNamespace="http://xx.com/framework/soa/sample/v1" elementFormDefault="qualified" attributeFormDefault="unqualified" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://xx.com/framework/soa/sample/v1" xmlns:ns="http://xx.com/framework/soa/sample/v1" xmlns:common="http://xx.com/common/types/v1">  <xs:import id="SOACommonTypes" schemaLocation="SOACommonTypes_V1.0.0.xsd" namespace="http://xx.com/common/types/v1" />  <xs:include schemaLocation="FoodOrder.xsd" />  <xs:include schemaLocation="PayType.xsd" />  <xs:element name="FoodOrderRequest" nillable="true" type="ns:FoodOrderRequestType" />  <xs:element name="FoodOrderResponse" nillable="true" type="ns:FoodOrderResponseType" /></xs:schema>

自定义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;    /// <summary>    /// 生成XSD的文件名称    /// </summary>    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<XsdImport> Get(Assembly assembly)    {        var attributes = GetCustomAttributes(assembly, typeof (XsdImport));        return attributes.Cast<XsdImport>();    }}

2. Class类型的Attribute

[AttributeUsage((Attribut
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表