系统之间的数据交互往往需要事先定义一些契约,在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个文件:
<?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>
<?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>
<?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>
[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>(); }}
[AttributeUsage((Attribut
新闻热点
疑难解答