2012-04-12 13 views
16

Aquí se presenta una XSD:Uso de XSDs con incluye

<?xml version="1.0"?> 
<xsd:schema 
elementFormDefault='unqualified' 
attributeFormDefault='unqualified' 
xmlns:xsd='http://www.w3.org/2001/XMLSchema' 
> 

    <xsd:simpleType name='TheSimpleType'> 
    <xsd:restriction base='xsd:string' /> 
    </xsd:simpleType> 
</xsd:schema> 

Aquí está una segunda XSD que incluye la de arriba:

<?xml version="1.0" encoding="UTF-8" ?> 
<xsd:schema 
elementFormDefault='unqualified' 
attributeFormDefault='unqualified' 
xmlns:xsd='http://www.w3.org/2001/XMLSchema' 
targetNamespace='a' 
xmlns='a' 
> 

    <xsd:include schemaLocation='Include.xsd' /> 

    <xsd:element name = "TheElement" > 
    <xsd:complexType> 
    <xsd:attribute name="Code" type="TheSimpleType" use="required"/> 
    </xsd:complexType> 
    </xsd:element> 
</xsd:schema> 

Necesito leer la (segunda) XSD en C# y:

  1. cheque que se trata de un XSD válido y
  2. documentos validar su contra.

Aquí es un poco de C# para leer en los esquemas:

XmlSchemaSet schemaSet = new XmlSchemaSet(); 
    foreach (string sd in Schemas) 
    { 
     using (XmlReader r = XmlReader.Create(new FileStream(sd, FileMode.Open))) 
     { 
      schemaSet.Add(XmlSchema.Read(r, null)); 
     } 
    } 
    schemaSet.CompilationSettings = new XmlSchemaCompilationSettings(); 
    schemaSet.Compile(); 

El .Compile() falla porque "Tipo 'a: TheSimpleType' no se declara, o no es un tipo simple."

Sin embargo, funciona si:

  • el espacio de nombres se retira del esquema o
  • el espacio de nombres se añade al incluir.

La pregunta es: ¿cómo consigo que C# lo acepte sin editar los esquemas?

Sospecho que el problema es que aunque he puesto ambos esquemas en el XmlSchemaSet, aún necesito decirle a C# que uno está incluido en el otro, es decir, que no lo ha resuelto por sí mismo. De hecho, si solo le digo a XmlSchemaSet sobre el XSD principal (y no el incluido) (ambos sin (o con) espacios de nombres), entonces "Tipo 'TheSimpleType' no está declarado, o no es un tipo simple."

Por lo tanto, esto parece ser una pregunta sobre la resolución incluye: ¿cómo?

Respuesta

4

Puede usar el XmlSchema.Includes para vincularlos. A continuación, sólo tiene que añadir el esquema principal de la serie de esquemas:

var includeSchema = XmlSchema.Read(XmlReader.Create(...), null); 
var mainSchema = XmlSchema.Read(XmlReader.Create(...), null); 

var include = new XmlSchemaInclude(); 
include.Schema = includeSchema; 
mainSchema.Includes.Add(include); 

var schemaSet = new XmlSchemaSet(); 
schemaSet.Add(mainSchema); 
schemaSet.Compile(); 
+1

+1 nunca supe la clase 'XmlSchemaInclude'. Gran respuesta. – psubsee2003

+2

OK, bien. Pero ahora supongamos que tengo que determinar todas las inclusiones en tiempo de ejecución, es decir, le doy un XSD arbitrario con includes y debe ir a buscarlas todas. –

+0

's = XmlSchema.Read (r, null);' Ahora veo que tenemos 's.Includes' que son objetos' XmlSchemaInclude', y está correctamente rellenado (con 1 include). –

0

Prueba esto: D

public static XmlSchema LoadSchema(string pathname) 
{ 
    XmlSchema s = null; 
    XmlValidationHandler h = new XmlValidationHandler(); 
    using (XmlReader r = XmlReader.Create(new FileStream(pathname, FileMode.Open))) 
    { 
     s = XmlSchema.Read(r, new ValidationEventHandler(h.HandleValidationEvent)); 
    } 

    if (h.Errors.Count > 0) 
    { 
     throw new Exception(string.Format("There were {1} errors reading the XSD at {0}. The first is: {2}.", pathname, h.Errors.Count, h.Errors[0])); 
    } 

    return s; 
} 

public static XmlSchema LoadSchemaAndResolveIncludes(string pathname) 
{ 
    FileInfo f = new FileInfo(pathname); 
    XmlSchema s = LoadSchema(f.FullName); 

    foreach(XmlSchemaInclude i in s.Includes) 
    { 
     XmlSchema si = LoadSchema(f.Directory.FullName + @"\" + i.SchemaLocation); 
     si.TargetNamespace = s.TargetNamespace; 
     i.Schema = si; 
    } 

    return s; 
} 

public static List<ValidationEventArgs> Validate(string pathnameDocument, string pathnameSchema) 
{ 
    XmlSchema s = LoadSchemaAndResolveIncludes(pathnameSchema); 

    XmlValidationHandler h = new XmlValidationHandler(); 

    XmlDocument x = new XmlDocument(); 
    x.Load(pathnameDocument); 
    x.Schemas.Add(s); 
    s.Compile(new ValidationEventHandler(h.HandleValidationEvent)); 
    x.Validate(new ValidationEventHandler(h.HandleValidationEvent)); 
    return h.Errors; 
} 

Nota en particular, la si.TargetNamespace = s.TargetNamespace;.

Obviamente, esto supone que las inclusiones se especifican como rutas de archivos relativas al esquema en el que se incluyen.

+0

Esto falla en el caso cuando (A incluye B y C) y (B hace referencia pero no incluye C). De hecho, es necesario escribir un XmlResolver personalizado para resolver los nombres de archivo. –

25

El problema es con la forma en que se abre el esquema para la lectura en la línea:

XmlReader.Create(new FileStream(sd, FileMode.Open) 

que tenía que escribir mi propia XmlResolver antes de que pudiera ver cómo se resolvían las rutas de acceso a los archivos de inclusión: se era del directorio del ejecutable y no del directorio del esquema principal. El problema es que el esquema primario no obtenía su conjunto de BaseURI. He aquí cómo se debe abrir el esquema:

XmlReader.Create(new FileStream(pathname, FileMode.Open, FileAccess.Read),null, pathname) 
+0

Del mismo modo, gracias por ese Richard + 1. Pasé mucho tiempo tirando de mi pelo sobre este! – CountZero

+0

Esto funcionó para mí cuando utilicé 'System.IO.Path (pathname)' para el argumento 'baseUri' de Create. –

0

Este es el método que escribí para manejar la validación de xsd. Espero que esto ayude a alguien.

 /// <summary> 
     /// Ensure all xsd imported xsd documented are in same folder as master xsd 
     /// </summary> 
     public XsdXmlValidatorResult Validate(string xmlPath, string xsdPath, string xsdNameSpace) 
     { 
      var result = new XsdXmlValidatorResult(); 
      var readerSettings = new XmlReaderSettings {ValidationType = ValidationType.Schema}; 
      readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema; 
      readerSettings.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation; 
      readerSettings.Schemas.Add(null, xsdPath); 

      readerSettings.ValidationEventHandler += (sender, args) => 
       { 
        switch (args.Severity) 
        { 
         case XmlSeverityType.Warning: 
          result.Warnings.Add(args.Message); 
          break; 
         case XmlSeverityType.Error: 
          result.IsValid = false; 
          result.Warnings.Add(args.Message); 
          break; 
        } 
       }; 

      var reader = XmlReader.Create(xmlPath, readerSettings); 

      while (reader.Read()) { } 

      return result; 
     } 
+0

Esto no funciona en mi caso. Todos los archivos XSD (y XML) están en la misma carpeta. Sin embargo, me dice que los tipos definidos en el xsd incluido no están definidos – CeOnSql