2010-09-09 15 views
12

Estoy intentando generar un esquema xs: de cualquier tipo .net programáticamente. Sé que podría usar el reflejo y generarlo iterando sobre las propiedades públicas, pero ¿hay un camino integrado?¿Cómo puedo generar programáticamente un esquema xml a partir de un tipo?

Ejemplo:

[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; } 
} 

salida deseada:

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

I duda de que hay una manera de hacer esto en el caso general. Además, '[Serializable]' no es utilizado por XML Serializer. –

+0

@John no lo sabía, ¡gracias! –

Respuesta

9

Así que esto funciona, supongo que no era tan feo como parecía:

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

Todavía esperaba había una solución de 2 líneas por ahí, parece que hay debe ser, gracias por la punta @dtb


EDITAR Sólo por KIC ks, aquí está la versión de 2 líneas (auto humor desaprobación)

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

Acabo de encontrarme en un problema similar al tuyo. He intentado usar tu código, reemplazando 'new Schema()' con una variable 'XmlSchema' ya existente, pero no funciona. ¿Podrías explicar mejor cómo funciona tu solución? – pyon

+0

¿Hay algo en el XmlSchema ya existente? El importador de reflejo de soap es una clase interna utilizada por .Net Framework para servicios web, creo. Hay algo de documentación msdn en él. –

+0

Hola, estoy a punto de rechazar un [edit] (http://stackoverflow.com/suggested-edits/266125) porque modifica tu código. Es posible que desee comprobarlo (y el comentario asociado) para ver si son válidos. – Benjol

6

Mediante programación puede invocar xsd.exe:

  1. Añadir xsd.exe como referencia de ensamblado.
  2. using XsdTool;
  3. Xsd.Main(new[] { "myassembly.dll", "/type:MyNamespace.MyClass" });

También puede utilizar el reflector para mirar las XsdTool.Xsd.ExportSchemas método. Utiliza las clases públicas XmlReflectionImporter, XmlSchemas, XmlSchemaXmlSchemaExporter y XmlTypeMapping para crear un esquema a partir de tipos .NET.

Esencialmente se hace esto:

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(...); 
}     ↑ 

Usted debe ser capaz de personalizar la salida pasando un escritor adecuado para el método XmlSchema.Write.

+0

Eso es interesante, no sabía que pudieras hacer eso. No es realmente lo que estoy buscando. La razón para hacerlo programáticamente sería controlar la salida. –

+0

De hecho, ya lo hice. Es una especie de grupo, esperaba que alguien supiera una mejor manera. –

0

La herramienta de definición de esquema XML genera el esquema XML o las clases de tiempo de ejecución del lenguaje común de los archivos XDR, XML y XSD, o de las clases en un ensamblaje en tiempo de ejecución.

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

+1

Ejecutar un .exe no cuenta como programático. –

+0

Siempre puede ejecutar .exe desde su código. ;) – JBeurer

1

Creo que esto es lo que está buscando: Writing your own XSD.exe

código de endeudamiento desde arriba:

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); 
       } 
      } 
     } 
    } 
} 
+1

Seguramente por ahora, ¿sabes mejor que publicar una respuesta solo de enlace? –

+1

@JohnSaunders: Lo debatí unos minutos antes de publicarlo, pero no pude encontrar un resumen útil (aparte de publicar todo el programa para el blog). El único riesgo con esto es que esta publicación no se sincronice con las actualizaciones desde allí. ¿Que recomiendas? – Mrchief

+0

Eliminarlo antes de que se elimine para usted sería mi recomendación, a menos que pueda hacer un resumen. –

13

encontré la accepted answer generó un esquema incorrecto dado algunos de mis atributos. p.ej.No tuvo en cuenta los nombres personalizados para los valores de enumeración marcados con [XmlEnum(Name="Foo")]

Creo que este es el camino correcto (dado que su uso de XmlSerializer) y es bastante simple también:

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(); 

Código extraído de: http://blogs.msdn.com/b/youssefm/archive/2010/05/13/using-xml-schema-import-and-export-for-xmlserializer.aspx

+0

Saludos, gracias por la actualización. –

Cuestiones relacionadas