【问题标题】:How do I programmatically generate an xml schema from a type?如何以编程方式从类型生成 xml 架构?
【发布时间】:2010-09-09 20:33:52
【问题描述】:

我正在尝试以编程方式从任何 .net 类型生成 xs:schema。我知道我可以使用反射并通过迭代公共属性来生成它,但是有内置的方法吗?

例子:

[Serializable]
public class Person
{
    [XmlElement(IsNullable = false)] public string FirstName { get; set; }
    [XmlElement(IsNullable = false)] public string LastName { get; set; }
    [XmlElement(IsNullable = true)] public string PhoneNo { get; set; }
}

期望的输出:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="Person" type="Person" />
  <xs:complexType name="Person">
    <xs:sequence>
      <xs:element minOccurs="0" maxOccurs="1" form="unqualified" name="FirstName" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="1" form="unqualified" name="LastName" type="xs:string" />
      <xs:element minOccurs="0" maxOccurs="1" form="unqualified" name="PhoneNo" type="xs:string" />
    </xs:sequence>
  </xs:complexType>
</xs:schema>

【问题讨论】:

  • 我怀疑在一般情况下是否有办法做到这一点。此外,XML 序列化程序不使用[Serializable]
  • @John 不知道,谢谢!

标签: c# .net xml


【解决方案1】:

我发现accepted answer 在给定我的某些属性的情况下生成了不正确的架构。例如它忽略了标有[XmlEnum(Name="Foo")]的枚举值的自定义名称

我相信这是正确的方法(鉴于您使用 XmlSerializer)并且也很简单:

var schemas = new XmlSchemas();
var exporter = new XmlSchemaExporter(schemas);
var mapping = new XmlReflectionImporter().ImportTypeMapping(typeof(Person));
exporter.ExportTypeMapping(mapping);
var schemaWriter = new StringWriter();
foreach (XmlSchema schema in schemas)
{
    schema.Write(schemaWriter);
}
return schemaWriter.ToString();

代码提取自: http://blogs.msdn.com/b/youssefm/archive/2010/05/13/using-xml-schema-import-and-export-for-xmlserializer.aspx

【讨论】:

    【解决方案2】:

    所以这行得通,我想它并不像看起来那么难看:

    var soapReflectionImporter = new SoapReflectionImporter();
    var xmlTypeMapping = soapReflectionImporter.ImportTypeMapping(typeof(Person));
    var xmlSchemas = new XmlSchemas();
    var xmlSchema = new XmlSchema();
    xmlSchemas.Add(xmlSchema);
    var xmlSchemaExporter = new XmlSchemaExporter(xmlSchemas);
    xmlSchemaExporter.ExportTypeMapping(xmlTypeMapping);
    

    我仍然希望有一个 2 行解决方案,似乎应该有,谢谢@dtb 的提示


    编辑 只是为了好玩,这是 2 行版本(自贬幽默)

    var typeMapping = new SoapReflectionImporter().ImportTypeMapping(typeof(Person));
    new XmlSchemaExporter(new XmlSchemas { new XmlSchema() }).ExportTypeMapping(typeMapping);
    

    【讨论】:

    • 我刚刚发现自己遇到了与您类似的问题。我尝试使用您的代码,将new Schema() 替换为已经存在的XmlSchema 变量,但它不起作用。您能否进一步解释一下您的解决方案是如何工作的?
    • 现有的 XmlSchema 中有什么东西吗?我相信,Soap 反射导入器是 .Net Framework 用于 Web 服务的内部类。上面有一些 msdn 文档。
    • 嗨,我正要拒绝edit,因为它会修改您的代码。您可能需要检查它(以及相关的注释)以查看它们是否有效。
    【解决方案3】:

    您可以以编程方式调用xsd.exe

    1. 添加 xsd.exe 作为程序集引用。
    2. using XsdTool;
    3. Xsd.Main(new[] { "myassembly.dll", "/type:MyNamespace.MyClass" });

    您还可以使用 Reflector 查看 XsdTool.Xsd.ExportSchemas 方法。它使用公共 XmlReflectionImporterXmlSchemasXmlSchema XmlSchemaExporterXmlTypeMapping 类从 .NET 类型创建架构。

    基本上是这样的:

    var importer = new XmlReflectionImporter();
    var schemas = new XmlSchemas();
    var exporter = new XmlSchemaExporter(schemas);
    
    var xmlTypeMapping = importer.ImportTypeMapping(typeof(Person));
    exporter.ExportTypeMapping(xmlTypeMapping);
    
    schemas.Compile(..., false);
    
    for (var i = 0; i < schemas.Count; i++)
    {
        var schema = schemas[i];
        schema.Write(...);
    }                 ↑
    

    您应该能够通过将合适的编写器传递给XmlSchema.Write 方法来自定义输出。

    【讨论】:

    • 这很有趣,我不知道你能做到。不是我想要的。以编程方式执行此操作的原因是为了控制输出。
    • 我实际上已经这样做了。这有点像一个集群,我希望有人知道更好的方法。
    【解决方案4】:

    我相信这就是你要找的东西:Writing your own XSD.exe

    从上面借用代码:

    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Reflection;
    using System.Text;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Xml.Schema;
    using System.CodeDom;
    using System.CodeDom.Compiler;
    
    using Microsoft.CSharp;
    
    using NUnit.Framework;
    
    namespace XmlSchemaImporterTest
    {
      [TestFixture]
      public class XsdToClassTests
      {
          // Test for XmlSchemaImporter
          [Test]
          public void XsdToClassTest()
          {
              // identify the path to the xsd
              string xsdFileName = "Account.xsd";
              string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
              string xsdPath = Path.Combine(path, xsdFileName);
    
              // load the xsd
              XmlSchema xsd;
              using(FileStream stream = new FileStream(xsdPath, FileMode.Open, FileAccess.Read))
              {
                  xsd = XmlSchema.Read(stream, null);
              }
              Console.WriteLine("xsd.IsCompiled {0}", xsd.IsCompiled);
    
              XmlSchemas xsds = new XmlSchemas();
              xsds.Add(xsd);
              xsds.Compile(null, true);
              XmlSchemaImporter schemaImporter = new XmlSchemaImporter(xsds);
    
              // create the codedom
              CodeNamespace codeNamespace = new CodeNamespace("Generated");
              XmlCodeExporter codeExporter = new XmlCodeExporter(codeNamespace);
    
              List maps = new List();
              foreach(XmlSchemaType schemaType in xsd.SchemaTypes.Values)
              {
                  maps.Add(schemaImporter.ImportSchemaType(schemaType.QualifiedName));
              }
              foreach(XmlSchemaElement schemaElement in xsd.Elements.Values)
              {
                  maps.Add(schemaImporter.ImportTypeMapping(schemaElement.QualifiedName));
              }
              foreach(XmlTypeMapping map in maps)
              {
                  codeExporter.ExportTypeMapping(map);
              }
    
              RemoveAttributes(codeNamespace);
    
              // Check for invalid characters in identifiers
              CodeGenerator.ValidateIdentifiers(codeNamespace);
    
              // output the C# code
              CSharpCodeProvider codeProvider = new CSharpCodeProvider();
    
              using(StringWriter writer = new StringWriter())
              {
                  codeProvider.GenerateCodeFromNamespace(codeNamespace, writer, new CodeGeneratorOptions());
                  Console.WriteLine(writer.GetStringBuilder().ToString());
              }
    
              Console.ReadLine();
          }
    
          // Remove all the attributes from each type in the CodeNamespace, except
          // System.Xml.Serialization.XmlTypeAttribute
          private void RemoveAttributes(CodeNamespace codeNamespace)
          {
              foreach(CodeTypeDeclaration codeType in codeNamespace.Types)
              {
                  CodeAttributeDeclaration xmlTypeAttribute = null;
                  foreach(CodeAttributeDeclaration codeAttribute in codeType.CustomAttributes)
                  {
                      Console.WriteLine(codeAttribute.Name);
                      if(codeAttribute.Name == "System.Xml.Serialization.XmlTypeAttribute")
                      {
                          xmlTypeAttribute = codeAttribute;
                      }
                  }
                  codeType.CustomAttributes.Clear();
                  if(xmlTypeAttribute != null)
                  {
                      codeType.CustomAttributes.Add(xmlTypeAttribute);
                  }
              }
          }
      }
    }
    

    【讨论】:

    • 现在您肯定知道最好不要发布仅链接的答案吗?
    • @JohnSaunders:我在发布之前争论了几分钟,但我找不到有用的摘要(除了将整个程序发布到博客之外)。唯一的风险是这篇文章与那里的任何更新不同步。你有什么推荐?
    • 我的建议是在它被删除之前将其删除,除非你能想出一个摘要。
    【解决方案5】:

    XML 架构定义工具从 XDR、XML 和 XSD 文件或从运行时程序集中的类生成 XML 架构或公共语言运行时类。

    http://msdn.microsoft.com/en-us/library/x6c1kb0s(VS.71).aspx

    【讨论】:

    • 运行 .exe 不算作程序化。
    猜你喜欢
    • 2021-09-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-04
    • 1970-01-01
    相关资源
    最近更新 更多