Начало работы с проверкой XSD с помощью .NET

17

Вот моя первая попытка проверки XML с помощью XSD.

Файл XML, подлежащий проверке:

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

XSD, расположенный в «Schemas / config.xsd» относительно файла XML, подлежащего проверке:

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

Сейчас я просто хочу проверить файл XML точно так, как он отображается в настоящее время. Как только я пойму это лучше, я расширю еще больше. Нужно ли мне так много строк для чего-то такого простого, как XML-файл, как он существует в настоящее время?

Код проверки в 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;
        }

Выполнение приведенного выше кода дает это исключение:

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

Любое освещение будет оценено.

    
задан Nick Heiner 25.03.2010 в 05:18
источник

3 ответа

26

Вместо использования XDocument .Validate , я бы использовал XmlReader , который может быть настроен для обработки встроенной схемы с помощью XmlReaderSettings . Вы можете сделать что-то вроде следующего кода.

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

}

В приведенном выше коде предполагается использование следующих операторов.

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

Просто чтобы это было просто, я не возвращал boolean или коллекцию ошибок проверки, вы можете легко изменить это, чтобы сделать это.

Примечание. Я изменил ваши config.xml и config.xsd, чтобы проверить их. Это те изменения, которые я сделал.

config.xsd:

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

config.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="config.xsd">
    
ответ дан VoidDweller 31.03.2010 в 15:52
  • Первый ответ сработал для меня, за исключением того, что здесь не было обработки ошибок: // Разбор файла приведет к валидации. while (reader.Read ()); Некоторые ошибки (например, наличие элемента XML, но ничего другого) не пойманы. Хуже того, они даже не приводят к возникновению исключения в вызывающем коде. –  Mar 12 '12 at 18:12 12.03.2012 в 19:12
14

Ниже приведен рабочий образец:

Применение:

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

Класс:

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

Приведенный выше код проверяет следующий xml (code3) на 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>

При проверке на ваш xml / xsd я получаю ошибки, отличные от ваших; Я думаю, что это может помочь вам продолжить (добавить / удалить элементы xml) здесь:

Ошибки http://www.freeimagehosting.net/uploads/01a570ce8b.jpg

Вы также можете попробовать обратный процесс; попробуйте создать схему из вашего xml и сравнить с вашим фактическим xsd - см. разницу; и самый простой способ сделать это - использовать схему генерации с использованием VS IDE. Вот как вы это сделаете:

Создайте XSD из XML http://i44.tinypic.com/15yhto3.jpg

Надеюсь, что это поможет.

- EDIT -

Это по просьбе Джона, пожалуйста, см. обновленный код с использованием устаревших методов:

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

Применение:

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 (создан из 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>

Это гарантировано!

    
ответ дан KMån 31.03.2010 в 09:09
  • -10! Без использования блоков «новый XmlTextReader ()» устарел, «новый XmlValidatingReader ()» устарел, XmlSchemaCollection на самом деле устарел! Вы используете .NET 1.1 ??? –  John Saunders 31.03.2010 в 16:04
  • @John: Это решение проблемы; операционная система не указала какую-либо конкретную версию кода. Хотя, я удовлетворил ваши проблемы и добавил обновленный код. См. Редактирование. –  KMån 01.04.2010 в 07:41
  • Если кто-то не укажет версию, то вполне можно сказать, что они не означают .NET 1.1! Вы должны предположить, что они используют .NET 2.0, по крайней мере. Я удаляю нижний план. –  John Saunders 01.04.2010 в 10:29
  • В вашем коде использования средство проверки xml называется XMLValidator. В вашем фрагменте класса сам класс называется CXmlValidator. Должно быть одно и то же имя. –  Vertexwahn 26.04.2013 в 16:52
  • Какие методы не устарели - устарели в NET 4.0? –  Kiquenet 06.03.2017 в 13:35
2

Ваш код для извлечения расположения схемы выглядит странно. Почему вы получаете значение атрибута xmlns и объединяете его со значением атрибута xsi: noNamespaceSchemaLocation? Исключение вызвано тем, что вы не можете указать префикс при вызове атрибутов; вам нужно указать желаемое пространство имен X.

Попробуйте этот (непроверенный):

// 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);
                      });
    
ответ дан dtb 28.03.2010 в 21:07
  • Я понятия не имею, для чего нужен xmlns. Мне это даже нужно? Я просто хочу правильно указать расположение схемы из документа для проверки. –  Nick Heiner 28.03.2010 в 22:18
  • Я думаю, вам не нужно значение атрибута xmlns. Используйте xsi + "noNamespaceSchemaLocation", как показано выше. –  dtb 28.03.2010 в 22:20
  • Для записи xmlns используется анализатором xml, чтобы знать, что xsi в xsi: noNamespaceSchemaLocation относится к схеме http://www.w3.org/2001/XMLSchema-instance. После анализа документа вы используете XNamespace для обращения к пространствам имен, а атрибуты xmlns больше не актуальны. –  Zarat 14.01.2016 в 15:16