在 C# 中从 XML Schema 生成代码的限制是啥?

Posted

技术标签:

【中文标题】在 C# 中从 XML Schema 生成代码的限制是啥?【英文标题】:What are the limits to code generation from XML Schema in C#?在 C# 中从 XML Schema 生成代码的限制是什么? 【发布时间】:2011-06-18 02:06:20 【问题描述】:

我已经看到了几个关于使用xsd.exe 从 XML Schema 生成类的问题的问题,以及关于如何预处理模式(通常使用 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# 结构,而不是相反。不过很好的输入。

【问题讨论】:

@marc_s - 我对相反的方向更感兴趣,XML Schema -> C#,我没想到能够将 XML Schema 用作编程语言! xsd.exe 未完全映射所有 xml 架构约定。此外,某些映射并不完美 - 例如 元素。 【参考方案1】:

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

我将展示几个例子来说明我已经走了多远。我的演示将不完整(考虑到 XML Schema 规范相当全面),但应该足以展示......

可以xsd.exe做得更好(如果您在编写 XML Schema 时愿意遵守某些模式);和 XML Schema 允许不能用 C# 表达的类型声明。考虑到 XML 和 C# 是非常不同的语言,用途也大不相同,这不应该让人感到意外。

在 XML Schema 中声明接口

C# 接口可以在具有复杂类型的 XML Schema 中定义。例如:

<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>

相当好地对应于:

interface IFoo

    string Bar  get; set; 
    int?   Baz  get; set; 

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

注意映射的一些问题:

publicinternal 等 C# 访问修饰符无法在 XML Schema 中呈现。

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

您不能在 XML Schema 中定义方法。

您也无法表达 C# structclass 之间的区别。 (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>

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

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

abstract class FooBase  ... 

声明类

同上,但省略abstract="true"

声明实现接口的类

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

<xsd:element name="Foo" type="IFoo" />

这映射到:

interface IFoo  ... 

class Foo : IFoo  ... 

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

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

因为你不能定义方法(代码),并且因为你也不能指定访问修饰符,所以你不需要用 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>

这映射到:

class Base

    bool Foo  get; set; 


class Derived : Base

    string Bar  get; set; 

注意:

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

注意引用:元素DerivedBase 的替换组中;同时,复杂类型derived 是复杂类型base 的扩展。 Derived 的类型为 derivedBase 的类型为 base

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

我的回答中没有涉及到的一些事情

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

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

enums。 (它们在 XML Schema 中通过 enumeration 限制简单类型来实现,顺便说一句。)

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

如何将xsd:choicexsd:sequence映射到C#;如何正确映射IEnumerable&lt;T&gt;ICollection&lt;T&gt;IList&lt;T&gt;IDictionary&lt;TKey, TValue&gt; 到 XML Schema?

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

还有很多我没有展示的东西,但现在您可能已经看到了我的示例背后的基本模式。

要正确完成这一切,必须系统地阅读 XML Schema 规范,并了解其中提到的每个概念如何将 最佳 映射到 C#。 (也许没有单一的最佳解决方案,但有几个替代方案。)但我明确表示只展示几个有趣的例子。我希望这仍然提供足够的信息!

【讨论】:

一个有趣的答案,它处理了我的很多想法,尽管从相反的角度来看(C# => XML Schema 而不是 XML Schema => C#)。谢谢。【参考方案2】:

这不是代码生成的限制。就是 XML 模式不描述类。它描述了 XML,这是另一回事。

结果是 XML Schema 与 C# 类、Java 类或任何其他类型的类之间存在“阻抗不匹配”。两者不等价,也不应该是等价的。

【讨论】:

然而,使用 XML Schema 来提供代码大纲是很常见的。显然我们不能使用 XML Schema 来提供这样的接口,我对尝试在 XML Schema 中复制 C# 不感兴趣。我对从 XML Schema 到 C# 的可能概念映射可能在哪里分崩离析感兴趣。换句话说,阻抗失配只是一种方式吗? @James:你将如何映射xs:choice 或混合内容? @John Saunders - 这几乎是我一开始的问题!混合内容只是“轻微”的特殊情况,因为它是一系列文本节点和子元素。 @James:您如何将其映射到 OO?您将不得不使用IEnumerable&lt;XNode&gt; 或同样无用的东西。没有有用的映射。 @John Saunders:我感谢您在该领域的专业知识,并且忘记它的建议并不难遵循,因为我一开始就没有尝试过 :-) 我不过,我仍然对这篇长论文感兴趣!

以上是关于在 C# 中从 XML Schema 生成代码的限制是啥?的主要内容,如果未能解决你的问题,请参考以下文章

C#实体类生成XML与XML Schema文档

如何在 C# 中从 XML 中删除选定的节点?

在 Eclipse 中从 XML 生成 Java 代码

如何在 xcode (swift) 中从 Json Schema / Json 生成模型对象?

在 C# 中从 XML 中获取某个元素 [重复]

在C++中如何用schema校验xml文件