Pregunta

Este es mi primer intento de validar XML con XSD.

El archivo XML para ser validada:

<?xml version="1.0" encoding="utf-8" ?>
<config xmlns="Schemas" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd">
  <levelVariant>
    <filePath>SampleVariant</filePath>
  </levelVariant>
  <levelVariant>
    <filePath>LegendaryMode</filePath>
  </levelVariant>
  <levelVariant>
    <filePath>AmazingMode</filePath>
  </levelVariant>
</config>

El XSD, situada en "Esquemas / config.xsd" en relación con el archivo XML para ser validada:

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="config">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="levelVariant">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="filePath" type="xs:anyURI">
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Ahora mismo, sólo quiero para validar el archivo XML, precisamente, tal como aparece actualmente. Una vez que entender esto mejor, Voy a ampliar más. ¿Es realmente necesario tantas líneas para algo tan simple como el archivo XML, tal como existe actualmente?

El código de validación en C #:

        public void SetURI(string uri)
        {
            XElement toValidate = XElement.Load(Path.Combine(PATH_TO_DATA_DIR, uri) + ".xml");

// begin confusion

       // exception here
       string schemaURI = toValidate.Attributes("xmlns").First().ToString() 
                              + toValidate.Attributes("xsi:noNamespaceSchemaLocation").First().ToString();
        XmlSchemaSet schemas = new XmlSchemaSet();
        schemas.Add(null, schemaURI);

        XDocument toValidateDoc = new XDocument(toValidate);
        toValidateDoc.Validate(schemas, null);
// end confusion

            root = toValidate;
        }

Al ejecutar el código anterior da esta excepción:

The ':' character, hexadecimal value 0x3A, cannot be included in a name.

Cualquier iluminación sería apreciada.

¿Fue útil?

Solución

En lugar de utilizar el XDocument.Validate método de extensión, me gustaría utilizar un XmlReader que puede ser configurado para procesar un esquema en línea a través de XmlReaderSettings . Usted podría hacer algo como el siguiente código.

public void VerifyXmlFile(string path)
{
    // configure the xmlreader validation to use inline schema.
    XmlReaderSettings config = new XmlReaderSettings();
    config.ValidationType = ValidationType.Schema;
    config.ValidationFlags |= XmlSchemaValidationFlags.ReportValidationWarnings;
    config.ValidationFlags |= XmlSchemaValidationFlags.ProcessInlineSchema;
    config.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation;
    config.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);

    // Get the XmlReader object with the configured settings.
    XmlReader reader = XmlReader.Create(path, config);

    // Parsing the file will cause the validation to occur.
    while (reader.Read()) ;

}

private void ValidationCallBack(object sender, ValidationEventArgs vea)
{
    if (vea.Severity == XmlSeverityType.Warning)
        Console.WriteLine(
            "\tWarning: Matching schema not found.  No validation occurred. {0}",
            vea.Message);
    else
        Console.WriteLine("\tValidation error: {0}", vea.Message);

}

El código anterior se ha realizado utilizando el siguiente declaraciones.

using System.Xml;
using System.Xml.Schema;

Sólo para mantener este sencillo No volví un boolean o una colección de errores de validación, usted puede fácilmente modificar esto para hacerlo.

Nota: He modificado su config.xml y config.xsd para llegar a validar. Estos son los cambios que hice.

config.xsd:

<xs:element maxOccurs="unbounded" name="levelVariant">

config.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd">

Otros consejos

A continuación se presenta una muestra de trabajo:

Uso:

XMLValidator val = new XMLValidator();
if (!val.IsValidXml(File.ReadAllText(@"d:\Test2.xml"), @"D:\Test2.xsd"))
   MessageBox.Show(val.Errors);

Clase:

public class CXmlValidator
{
    private int nErrors = 0;
    private string strErrorMsg = string.Empty;
    public string Errors { get { return strErrorMsg; } }
    public void ValidationHandler(object sender, ValidationEventArgs args)
    {
        nErrors++;
        strErrorMsg = strErrorMsg + args.Message + "\r\n";
    }

    public bool IsValidXml(string strXml/*xml in text*/, string strXsdLocation /*Xsd location*/)
    {
        bool bStatus = false;
        try
        {
            // Declare local objects
            XmlTextReader xtrReader = new XmlTextReader(strXsdLocation);
            XmlSchemaCollection xcSchemaCollection = new XmlSchemaCollection();
            xcSchemaCollection.Add(null/*add your namespace string*/, xtrReader);//Add multiple schemas if you want.

            XmlValidatingReader vrValidator = new XmlValidatingReader(strXml, XmlNodeType.Document, null);
            vrValidator.Schemas.Add(xcSchemaCollection);

            // Add validation event handler
            vrValidator.ValidationType = ValidationType.Schema;
            vrValidator.ValidationEventHandler += new ValidationEventHandler(ValidationHandler);

            //Actual validation, read conforming the schema.
            while (vrValidator.Read()) ;

            vrValidator.Close();//Cleanup

            //Exception if error.
            if (nErrors > 0) { throw new Exception(strErrorMsg); }
            else { bStatus = true; }//Success
        }
        catch (Exception error) { bStatus = false; }

        return bStatus;
    }
}

El código anterior valida siguiente xml (code3) contra xsd (code4).

<!--CODE 3 - TEST1.XML-->
<address xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test1.xsd"> 
<name>My Name</name>
<street>1, My Street Address</street>
<city>Far</city>
<country>Mali</country>
</address>

<!--CODE 4 - TEST1.XSD-->
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="street" type="xs:string"/>
<xs:element name="city" type="xs:string"/>
<xs:element name="country" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

En la validación en contra de su XML / XSD consigo de errores diferente a la suya; Creo que esto puede ayudarle a continuar (elementos XML Add / Remove) desde aquí:

Errores

También puede probar el proceso inverso; intentar generar el esquema de su xml y comparar con su xsd real - ver la diferencia; y la forma más sencilla de hacerlo es utilizar generar esquema utilizando VS IDE. Lo que sigue es cómo se haría lo siguiente:

Crear XSD a partir de XML

Espero que esto ayude.

- Editar -

Esto es a petición de Juan, consulte código actualizado utilizando métodos no desuso:

public bool IsValidXmlEx(string strXmlLocation, string strXsdLocation)
{
    bool bStatus = false;
    try
    {
        // Declare local objects
        XmlReaderSettings rs = new XmlReaderSettings();
        rs.ValidationType = ValidationType.Schema;
        rs.ValidationFlags |= XmlSchemaValidationFlags.ProcessSchemaLocation | XmlSchemaValidationFlags.ReportValidationWarnings;
        rs.ValidationEventHandler += new ValidationEventHandler(rs_ValidationEventHandler);
        rs.Schemas.Add(null, XmlReader.Create(strXsdLocation));

        using (XmlReader xmlValidatingReader = XmlReader.Create(strXmlLocation, rs))
        { while (xmlValidatingReader.Read()) { } }

        ////Exception if error.
        if (nErrors > 0) { throw new Exception(strErrorMsg); }
        else { bStatus = true; }//Success
    }
    catch (Exception error) { bStatus = false; }

    return bStatus;
}

void rs_ValidationEventHandler(object sender, ValidationEventArgs e)
{
    if (e.Severity == XmlSeverityType.Warning) strErrorMsg += "WARNING: " + Environment.NewLine;
    else strErrorMsg += "ERROR: " + Environment.NewLine;
    nErrors++;
    strErrorMsg = strErrorMsg + e.Exception.Message + "\r\n";
}

Uso:

if (!val.IsValidXmlEx(@"d:\Test2.xml", @"D:\Test2.xsd"))
                MessageBox.Show(val.Errors);
            else
                MessageBox.Show("Success");

test2.xml

<?xml version="1.0" encoding="utf-8" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Test2.xsd">
  <levelVariant>
    <filePath>SampleVariant</filePath>
  </levelVariant>
  <levelVariant>
    <filePath>LegendaryMode</filePath>
  </levelVariant>
  <levelVariant>
    <filePath>AmazingMode</filePath>
  </levelVariant>
</config>

Test2.XSD (generado a partir VS IDE)

<?xml version="1.0" encoding="utf-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
  <xs:element name="config">
    <xs:complexType>
      <xs:sequence>
        <xs:element maxOccurs="unbounded" name="levelVariant">
          <xs:complexType>
            <xs:sequence>
              <xs:element name="filePath" type="xs:anyURI">
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Esto se garantiza que funcione!

Su código para extraer la ubicación del esquema se ve raro. ¿Por qué se obtiene el valor del atributo xmlns y concatena con el valor del atributo xsi: noNamespaceSchemaLocation? La excepción es causada por el hecho de que no se puede especificar un prefijo en una llamada a atributos; es necesario especificar el XNamespace deseada.

Probar (no probado):

// Load document
XDocument doc = XDocument.Load("file.xml");

// Extract value of xsi:noNamespaceSchemaLocation
XNamespace xsi = "http://www.w3.org/2001/XMLSchema-instance";
string schemaURI = (string)doc.Root.Attribute(xsi + "noNamespaceSchemaLocation");

// Create schema set
XmlSchemaSet schemas = new XmlSchemaSet();
schemas.Add("Schemas", schemaURI);

// Validate
doc.Validate(schemas, (o, e) =>
                      {
                          Console.WriteLine("{0}", e.Message);
                      });
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top