C#中XML Schema的代码生成有哪些限制?

Jam*_*ord 18 c# xml xsd code-generation

我已经看到了几个关于使用XML Schema生成类的问题的问题xsd.exe,以及如何预先处理模式(通常使用XSLT)来解决生成之前的一些棘手方面的建议.我的问题是,是否可以构建一个100%符合XML Schema的C#代码生成器.问题xsd.exe仅仅是它的实现问题,还是它们指向XML Schema和C#之间的根本不一致?

特别是,我对如何将XML Schema中的概念映射到C#感兴趣 - 什么是可接受的映射,哪些映射是有争议的,是否存在本质上不可映射的XML Schema构造,是否存在未充分利用的C#构造?是否存在可提供映射规则的合规性规范,以便可以实施和测试?

编辑:为了清楚起见,我完全清楚XML Schema不会为我提供完全实现的C#接口,我对它是否可以完全映射到C#类层次结构感兴趣.

编辑2:我添加了一笔小额奖金,因为我有兴趣获得更多细节.

编辑3:赏金仍然开放,但到目前为止走向stakx - 一个很好的答案,但主要是处理如何在XML Schema中复制C#结构,而不是相反.虽然输入很好.

sta*_*ica 26

有趣的问题.不久前,我想知道完全相同的事情.

我将展示一些我有多远的例子.我的演示将不完整(考虑到XML Schema规范相当全面),但它应该足以显示......

  • 可以做的比更好xsd.exe(如果你愿意,当你写你的XML Schema来遵守一定的模式); 和
  • XML Schema允许,不能在C#中表达的类型声明.考虑到XML和C#是非常不同的语言,目的不同,这不应该是一个大惊喜.

在XML Schema中声明一个接口

可以使用复杂类型在XML Schema中定义C#接口.例如:

<xsd:complexType name="IFoo" abstract="true">
  <xsd:attribute name="Bar" type="xsd:string" use="required" />
  <xsd:attribute name="Baz" type="xsd:int" use="optional" />
</xsd:complexType>
Run Code Online (Sandbox Code Playgroud)

对应得很好:

interface IFoo
{
    string Bar { get; set; }
    int?   Baz { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

这里的模式是抽象和命名(非匿名)复杂类型基本上是C#中接口的XML Schema等价物.

请注意映射的一些问题:

  • C#访问修饰符如public,internal等无法在XML架构来呈现.

  • 您无法在XML Schema中表达C#字段和属性之间的区别.

  • 您无法在XML Schema中定义方法.

  • 你也无法表达C#struct和C#之间的区别class.(XML Schema中有简单的类型,它大致对应于.NET值类型;但它们在XML Schema中比复杂类型更受限制.)

  • 用法usage="optional"可用于映射可空类型.在XML Schema中,您可以将字符串属性定义为可选.转换到C#时,会发生一些转换损失:由于string是引用类型,因此不能将其声明为可为空(因为默认情况下它已经可以为空).

  • XML Schema也允许usage="prohibited".这又是一个无法用C#表达的东西,或者至少是一种不错的方式(AFAIK).

  • 从我的实验,似乎xsd.exe永远不会产生从抽象复杂类型C#接口; 它将与abstract classes 保持一致.(我猜这是为了使翻译逻辑保持相当简单.)

声明抽象类

抽象类可以与接口非常相似地完成:

<xsd:element name="FooBase" abstract="true">
  <xsd:complexType>
    ...
  </xsd:complexType>
</xsd:element>
Run Code Online (Sandbox Code Playgroud)

在这里,您定义一个abstract属性设置为的元素true,并在其中嵌入一个匿名复杂类型.

这对应于C#中的以下类型声明:

abstract class FooBase { ... }
Run Code Online (Sandbox Code Playgroud)

宣布课程

如上所述,但省略了abstract="true".

声明实现接口的类

<xsd:complexType name="IFoo" abstract="true">
  ...
</xsd:complexType>

<xsd:element name="Foo" type="IFoo" />
Run Code Online (Sandbox Code Playgroud)

这映射到:

interface IFoo { ... }

class Foo : IFoo { ... }
Run Code Online (Sandbox Code Playgroud)

也就是说,您既定义了命名的抽象复杂类型(接口),又定义了具有该类型的命名元素.

  • 请注意,上面的C#代码段包含...两次,而XML Schema代码段只有一个....怎么会?

    因为您无法定义方法(代码),并且因为您也无法指定访问修饰符,所以您不需要使用XML Schema中的元素"实现"复杂类型.复杂类型的"实现"与原始声明相同.如果复杂类型定义了一些属性,则这些属性只会映射到C#接口实现中的自动属性.

在XML Schema中表达继承关系

XML Schema中的类和接口继承可以通过类型扩展和元素替换组的组合来定义:

<xsd:element name="Base" type="base" />
<xsd:element name="Derived" substitutionGroup="Base" type="derived" />
                       <!-- ^^^^^^^^^^^^^^^^^^^^^^^^ -->

<xsd:complexType name="base">
  <xsd:attribute name="Foo" type="xsd:boolean" use="required" />
</xsd:complexType>

<xsd:complexType name="derived">
  <xsd:complexContent>
    <xsd:extension base="base">  <!-- !!! -->
      <xsd:attribute name="Bar" type="xsd:string" use="required" />
    </xsd:extension>
  </xsd:complexContent>
</xsd:complexType>
Run Code Online (Sandbox Code Playgroud)

这映射到:

class Base
{
    bool Foo { get; set; }
}

class Derived : Base
{
    string Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

注意:

  • 我们再次使用命名复杂类型.但是这一次,它们没有被定义abstract="true",因为我们没有定义任何C#接口类型.

  • 注意引用:元素DerivedBase替换组中; 同时,复杂类型derived是复杂类型的扩展base.Derived有类型derived,Base有类型base.

  • 非抽象的命名复杂类型在C#中没有直接对应的.它们不是类,因为它们不能被实例化(在XML中,元素,而不是类型,与F#中的值构造函数或C#中的对象实例化具有大致相同的功能); 它们都不是真正的接口,因为它们不是抽象的.

我在答案中没有涉及的一些事情

  • 展示如何在XML Schema中声明实现多个接口的C#类类型.

  • 显示XML Schema中的复杂内容如何映射到C#(我首先猜测C#中没有对应关系;至少在一般情况下不是这样).

  • enum秒.(通过限制简单类型来实现它们在XML Schema中enumeration,顺便说一下.)

  • const类中的字段(这些字段可能映射到具有fixed值的属性).

  • 如何映射xsd:choice,xsd:sequence到C#; 如何正确地映射IEnumerable<T>,ICollection<T>,IList<T>,IDictionary<TKey, TValue>XML模式?

  • XML Schema简单类型,听起来像是.NET值类型的相应概念; 但是受到更多限制并且有不同的目的.

还有很多我没有展示过的东西,但到现在为止你可能会看到我的例子背后的基本模式.

要正确地完成所有这些操作,必须系统地完成XML Schema规范并查看其中提到的每个概念如何最好地映射到C#.(可能没有单一的最佳解决方案,但有几种选择.)但我明确表示只展示几个有趣的例子.我希望这仍然足够丰富!