c#
  • xml
  • xsd
  • xml-namespaces
  • 2012-04-12 5 views 16 likes 
    16

    Ecco un XSD:Uso XSD con include

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

    Ecco un secondo XSD che include quello di cui sopra:

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

    ho bisogno di leggere il (secondo) XSD in C# e:

    1. verifica che si tratta di un XSD valido e
    2. documenti Convalida contro di essa.

    Ecco alcuni C# per leggere gli schemi:

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

    Il .Compile() fallisce perché "Tipo 'una: TheSimpleType' non è dichiarato, o non è un tipo semplice."

    Tuttavia, funziona se:

    • lo spazio dei nomi viene rimosso dallo schema, o
    • lo spazio dei nomi viene aggiunto al includere.

    La domanda è: come posso ottenere C# per accettarlo senza modificare gli schemi?

    Sospetto che il problema sia che sebbene abbia inserito entrambi gli schemi in XmlSchemaSet, devo comunque dire a C# che uno è incluso nell'altro, cioè non ha funzionato da solo. Infatti, se dico solo a XmlSchemaSet l'XSD principale (e non l'inclusione) (entrambi senza (o con) spazi dei nomi), allora "Type" TheSimpleType "non è dichiarato, o non è un tipo semplice."

    Quindi questa sembra essere una domanda sulla risoluzione include: come ?!

    risposta

    4

    È possibile utilizzare lo XmlSchema.Includes per collegarli tra loro. Poi, basta aggiungere lo schema principale per l'insieme schema:

    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 mai saputo della classe 'XmlSchemaInclude'. Bella risposta. – psubsee2003

    +2

    OK, bene. Ma ora supponiamo di dover determinare tutti gli include in fase di esecuzione, cioè, ti do un XSD arbitrario con include e devi andare a prenderli tutti. –

    +0

    's = XmlSchema.Read (r, null);' Ora vedo che 's.Include', che sono oggetti' XmlSchemaInclude', ed è compilato correttamente (con l'inclusione di 1). –

    0

    Prova questa: 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 in particolare la si.TargetNamespace = s.TargetNamespace;.

    Ovviamente, ciò presuppone che gli include siano specificati come percorsi di file relativi allo schema in cui sono inclusi.

    +0

    Questo fallisce nel caso in cui (A include B e C) e (riferimenti B ma non include C). È infatti necessario scrivere un XmlResolver personalizzato per risolvere i nomi dei file. –

    25

    Il problema è con il modo in cui lo schema è aperto per la lettura sulla linea:

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

    ho dovuto scrivere il mio XmlResolver prima ho potuto vedere come i percorsi dei file include venivano risolti: è proveniva dalla directory dell'eseguibile e non dalla directory dello schema padre. Il problema è che lo schema padre non ha ottenuto il set BaseURI. Ecco come lo schema deve essere aperto:

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

    Allo stesso modo, grazie per quello di Richard + 1. Ho passato molto tempo a strapparmi i capelli da questo! – CountZero

    +0

    Questo ha funzionato per me quando ho usato 'System.IO.Path (pathname)' per l'argomento 'baseUri' di Create. –

    0

    Ecco il metodo che ho scritto per gestire la convalida xsd. Spero che questo aiuti qualcuno.

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

    Questo non funziona nel mio caso. Tutti i file XSD (e XML) si trovano nella stessa cartella. Tuttavia, mi dice che i tipi definiti nel xsd incluso non sono definiti – CeOnSql

     Problemi correlati

    • Nessun problema correlato^_^