在此过程中涉及到四个类,它们都定义在 system.xml.serialization 命名空间中:
此主题相关图片如下:
图 1. 用于获得 codedom 树的类
可以按以下方式,使用这些类来获得 codedom 树:
namespace xsdgenerator
{
public sealed class processor
{
public static codenamespace process( string xsdfile,
string targetnamespace )
{
// load the xmlschema and its collection.
xmlschema xsd;
using ( filestream fs = new filestream( xsdfile, filemode.open ) )
{
xsd = xmlschema.read( fs, null );
xsd.compile( null );
}
xmlschemas schemas = new xmlschemas();
schemas.add( xsd );
// create the importer for these schemas.
xmlschemaimporter importer = new xmlschemaimporter( schemas );
// system.codedom namespace for the xmlcodeexporter to put classes in.
codenamespace ns = new codenamespace( targetnamespace );
xmlcodeexporter exporter = new xmlcodeexporter( ns );
// iterate schema top-level elements and export code for each.
foreach ( xmlschemaelement element in xsd.elements.values )
{
// import the mapping first.
xmltypemapping mapping = importer.importtypemapping(
element.qualifiedname );
// export the code finally.
exporter.exporttypemapping( mapping );
}
return ns;
}
}
}
这些代码非常简单,尽管您可能希望在其中添加异常管理代码。需要注意的一件事情是 xmlschemaimporter 通过使用类型的限定名来导入类型,然后将其放在相应的 xmlschema 中。因此,必须将架构中的所有全局元素传递给它,然后使用 xmlschema.elements 集合进行迭代。该集合像 xmlschemaelement.qualifiedname 一样,也是在架构编译之后被填充的所谓的 post schema compilation infoset(即 psci,请参阅 msdn 帮助)的成员。它具有在解析引用、架构类型、继承、包含等之后填充和组织架构信息的作用。其功能类似于 dom post validation infoset(即 psvi,请参阅 dare obasanjo 的 msdn 文章和 xsd 规范)。
您可能已经注意到 xmlschemaimporter 工作方式的一个副作用(实际上是一个缺陷):您只能检索(导入)全局定义的元素的映射。在架构中的任何位置局部定义的任何其他元素将无法通过该机制访问。这具有我将在后面讨论的一些后果,它们可能会限制您可以应用的自定义,或者影响我们的架构设计。
xmlcodeexporter 类根据所导入的映射,用类型定义来填充传递给其构造函数的 codedomnamespace,从而生成所谓的 codedom 树。通过上述方法得到的 codedom 就是 xsd.exe 工具在内部生成的东西。有了该树以后,就可以直接将其编译为程序集,或者生成源代码。
如果我希望摆脱 xsd.exe 工具,可以轻松地生成使用该类的控制台应用程序。为达到该目的,我需要根据收到的 codedom 树生成一个源代码文件。我通过创建一个适用于用户所选的目标语言的 codedomprovider 来做到这一点:
static void main( string[] args )
{
if ( args.length != 4 )
{
console.writeline(
"usage: xsdgenerator xsdfile namespace outputfile [cs|vb]" );
return;
}
// get the namespace for the schema.
codenamespace ns = processor.process( args[0], args[1] );
// create the appropriate generator for the language.
codedomprovider provider;
if ( args[3] == "cs" )
provider = new microsoft.csharp.csharpcodeprovider();
else if ( args[3] == "vb" )
provider = new microsoft.visualbasic.vbcodeprovider();
else
throw new argumentexception( "invalid language", args[3] );
// write the code to the output file.
using ( streamwriter sw = new streamwriter( args[2], false ) )
{
provider.creategenerator().generatecodefromnamespace(
ns, sw, new codegeneratoroptions() );
}
console.writeline( "finished" );
console.read();
}
我可以使用生成器所收到的 codegeneratoroptions 实例的属性,进一步自定义生成的代码格式和其他选项。有关可用的选项,请参阅 msdn 文档。
在编译该控制台应用程序后,我可以生成与 xsd.exe 工具所生成的完全相同的代码。有了这一功能,使我完全不必再依赖该工具,并且我不再需要知道该工具是否已安装或者位于何处,也不再需要为它启动新的进程,等等。然而,每当我修改架构以后,都需要一遍遍地从命令行运行它,这是很不理想的。microsoft?visual studio?.net 使开发人员可以通过所谓的自定义工具来利用设计时代码生成。其中一个例子是类型化数据集,当您使用它时(尽管不必具体指定),都会有一个自定义工具在您每次保存数据集 xsd 文件时对其进行处理,并自动生成相应的“代码隐藏”类。
有关构建自定义工具的内容超出了本文的范围,但您可以阅读更多有关将我迄今为止所编写的代码转换为该网络日记张贴中的自定义工具的内容。该工具的代码包含在本文的下载内容中,您可以通过将“xsdcodegen”自定义工具名称指定给 xsd 文件属性来简单地使用它。注册方法在随附的自述文件中进行了说明。
即使我能够找到更容易使用的自定义工具,但是将 xsd.exe 工具替换为另一个执行完全相同任务的工具并没有太大意义,不是吗?毕竟,我们完成这些工作的原因就是为了改变这种做法!因此,让我们从这一底线开始对其进行自定义。
扩展 xsd 处理
为了自定义处理过程,我需要将信息传递给该工具,以便它知道要更改或处理的内容。此时有两种主要选择: