Проверка достоверности XML

22

API-интерфейс для работы с XML был бы неполным без возможности проверки достоверности XML. В LINQ to XML имеются средства проверки XML-документа на соответствие схеме XML.

Потребность в проверке достоверности в LINQ to XML реализуется за счет создания статического класса System.Xml.Schema.Extensions, содержащего методы проверки достоверности. Эти методы проверки достоверности реализованы в виде расширяющих методов.

Ниже приведен список некоторых доступных методов проверки достоверности в классе System.Xml.Schema.Extensions:

void Extensions.Validate (this XDocument source, XmlSchemaSet schemas, 
         ValidationEventHandler validationEventHandler)

void Extensions.Validate (this XDocument source, XmlSchemaSet schemas, 
         ValidationEventHandler validationEventHandler, bool addSchemaInfo)
         
void Extensions.Validate(this XElement source, 
         XmlSchemaObject partialValidationType, XmlSchemaSet schemas, 
         ValidationEventHandler validationEventHandler)

void Extensions.Validate(this XElement source, 
         XmlSchemaObject partialValidationType, XmlSchemaSet schemas, 
         ValidationEventHandler validationEventHandler, bool addSchemaInfo)

void Extensions.Validate(this XAttribute source, 
         XmlSchemaObject partialValidationType, 
         XmlSchemaSet schemas, 
         ValidationEventHandler validationEventHandler)

void Extensions.Validate(this XAttribute source, 
         XmlSchemaObject partialValidationType, XmlSchemaSet schemas, 
         ValidationEventHandler validationEventHandler, bool addSchemaInfo) 

Для каждого типа объектов, на которых может быть вызван метод, предусмотрено по два прототипа. Эти типы — XDocument, XElement и XAttribute. Во втором прототипе для каждого типа объекта просто предусмотрен аргумент bool для указания того, должна ли добавляться информация схемы к объектам XElement и XAttribute после проверки достоверности. Первый метод для каждого из типов объектов, который не имеет аргумента bool, работает так же, как второй, когда в аргументе addShemaInfo ему передается false. В этом случае после проверки достоверности в объекты LINQ to XML никакой информации схемы не добавляется.

Чтобы получить информацию схемы для объектов XElement или XAttribute, вызывайте метод GetSchemaInfo на объекте. Если информация схемы не добавлена, либо в результате применения первого прототипа, либо второго с передачей false в аргументе addShemaInfo, то метод GetSchemaInfo вернет null. В противном случае он вернет объект, реализующий IXmlSchemaInfo. Этот объект будет содержать свойство по имени SchemaElement, которое вернет объект XmlSchemaAttribute, если предположить, что элемент или атрибут допустим. Эти объекты могут использоваться для получения дополнительной информации о схеме.

Важно отметить, что информация о схеме не доступна во время проверки достоверности, а только после ее завершения. Это значит, что получить информацию о схеме в обработчике события проверки достоверности нельзя. В этом случае вызов метода GetSchemaInfo вернет null. Это также означает, что проверка достоверности должна полностью завершиться, и что внутри обработчика события проверки достоверности не допускается генерировать исключения.

Вызов GetSchemaInfo внутри обработчика события проверки достоверности вернет null.

Обратите внимание, что прототипы метода Validate для элементов и атрибутов требуют передачи XmlSchemaObject в качестве одного из аргументов. Это значит, что документ, в котором они находятся, уже должен быть проверен.

И, наконец, если вы передадите null в аргументе ValidationEventHandler, то в случае ошибки проверки достоверности будет сгенерировано исключение типа XmlSchemaValidationException. Это будет простейшим подходом к организации проверки достоверности XML-документа.

Получение схемы XML

Для проверки достоверности XML-документа понадобится иметь файл схемы XSD или знать, как его получить. Ниже будет показано, как в этом может помочь .NET Framework. Рассмотрим пример ниже:

XDocument xDocument = new XDocument(
              new XElement("Employees",
                     new XElement("Employee",
                       new XAttribute("type", "Programmer"),
                       new XElement("FirstName", "Alex"),
                       new XElement("LastName", "Erohin")),
                     new XElement("Employee",
                       new XAttribute("type", "Editor"),
                       new XElement("FirstName", "Elena"),
                       new XElement("LastName", "Volkova"))));

            Console.WriteLine("Исходный XML-документ: \n");
            Console.WriteLine(xDocument + "\n");

            xDocument.Save("employees.xml");

            XmlSchemaInference infer = new XmlSchemaInference();
            XmlSchemaSet schemaSet =
              infer.InferSchema(new XmlTextReader("employees.xml"));

            XmlWriter w = XmlWriter.Create("employees.xsd");
            foreach (XmlSchema schema in schemaSet.Schemas())
            {
                schema.Write(w);
            }
            w.Close();

            XDocument newDocument = XDocument.Load("employees.xsd");
            Console.WriteLine("\nИзвлеченная схема: \n");
            Console.WriteLine(newDocument);

В приведенном коде сначала создается обычный XML-документ, который уже использовался во многих примерах, и отображается для целей ознакомления. Затем XML-документ сохраняется на диске. После этого создается экземпляр объекта XmlSchemaInference и строится XmlSchemaSet вызовом метода InferSchema на объекте XmlSchemaInference. Далее создается объект для записи и выполняется перечисление XmlSchemaSet с записью каждой схемы в файл employees.xsd. И, наконец, сгенерированный файл схемы XSD загружается и отображается. Результат выполнения показан ниже:

Создание схемы XSD за счет ее извлечения из документа XML

Получить схему подобным образом совсем не трудно. Далее сгенерированный файл XSD схемы по имени employees.xsd используется в примерах проверки достоверности. Также обратите внимание, что в данном примере применяется класс XmlSchemaSet, который также будет участвовать в примерах проверки достоверности.

Примеры

В первом примере демонстрируются простейшие средства проверки достоверности документа XML — именно такому подходу отдает предпочтение большинство разработчиков. Для этого нужно просто указать null в качестве аргумента ValidationEventHandler:

XDocument xDocument = new XDocument(
              new XElement("Employees",
                     new XElement("Employee",
                       new XAttribute("type", "Programmer"),
                       new XElement("Age",26),
                       new XElement("FirstName", "Alex"),
                       new XElement("LastName", "Erohin")),
                     new XElement("Employee",
                       new XAttribute("type", "Editor"),
                       new XElement("FirstName", "Elena"),
                       new XElement("LastName", "Volkova"))));

            Console.WriteLine("Исходный XML-документ: \n");
            Console.WriteLine(xDocument + "\n");

            XmlSchemaSet schemaSet = new XmlSchemaSet();
            schemaSet.Add(null, "employees.xsd");
            try
            {
                xDocument.Validate(schemaSet, null);
                Console.WriteLine("Проверка достоверности документа завершена успешно");
            }
            catch (XmlSchemaValidationException ex)
            {
                Console.WriteLine("Произошло исключение:   {0}", ex.Message); 
                Console.WriteLine("Документ не прошел проверку достоверности.");
            }

В этом примере конструируется обычный XML-документ, в который преднамеренно добавляется элемент Age, чтобы сделать его недопустимым. Здесь используется схема, полученная в предыдущем примере. Обратите внимание, что в аргументе ValidationEventHandler метода Validate передается null. Это значит, что если возникнет ошибка проверки достоверности, будет автоматически сгенерировано исключение XmlSchemaValidationException. И вот результат:

Проверка достоверности документа XML с обработкой события проверки по умолчанию

Все очень просто и нормально работает.

В следующем примере выполняется проверка достоверности типичного XML-документа - того, который был использован для генерации схемы (без элемента Age). Конечно, поскольку схема получена на основе самого XML-документа, она должна работать. Однако для этого примера понадобится метод ValidationEventHandler. Ниже представлен код видоизмененного примера:

// Обработчик ValidationEventHandler
static void MyValidationEventHandler(object o, ValidationEventArgs vea)
{
        Console.WriteLine("Ошибка проверки достоверности во время обработки объекта " + 
             o.GetType().Name);
        Console.WriteLine(vea.Message);
        throw (new Exception(vea.Message));
}

...

XDocument xDocument = new XDocument(
              new XElement("Employees",
                     new XElement("Employee",
                       new XAttribute("type", "Programmer"),
                       //new XElement("Age",26),
                       new XElement("FirstName", "Alex"),
                       new XElement("LastName", "Erohin")),
                     new XElement("Employee",
                       new XAttribute("type", "Editor"),
                       new XElement("FirstName", "Elena"),
                       new XElement("LastName", "Volkova"))));

            Console.WriteLine("Исходный XML-документ: \n");
            Console.WriteLine(xDocument + "\n");

            XmlSchemaSet schemaSet = new XmlSchemaSet();
            schemaSet.Add(null, "employees.xsd");
            try
            {
                xDocument.Validate(schemaSet, MyValidationEventHandler);
                Console.WriteLine("Проверка достоверности документа завершена успешно");
            }
            catch (XmlSchemaValidationException ex)
            {
                Console.WriteLine("Произошло исключение:   {0}", ex.Message); 
                Console.WriteLine("Документ не прошел проверку достоверности.");
            }

В обработчике MyValidationEventHandler не делается ничего помимо вывода сообщения о проблеме и генерации исключения. Конечно, то, как будет выполнена обработка, полностью зависит от обработчика. Нет необходимости генерировать исключение.

Далее в коде создается типичный XML-документ, который сразу отображается на консоли. Затем создается экземпляр объекта XmlSchemaSet и добавляется созданный ранее файл схемы с помощью метода Add. После этого на XML-документе вызывается расширяющий метод Validate с передачей ему набора схем и метода-обработчика события проверки достоверности. Обратите внимание, что в целях безопасности вызов метода Validate помещен в блок try/catch. Взглянем на результат:

Проверка достоверности документа XML с использованием обработчика ValidationEventHandler

Теперь раскомментируем строчку с добавлением элемента Age в вышеприведенном примере и посмотрим, как отслеживает проверку обработчик MyValidationEventHandler:

...
        new XElement("Age",26),
...
Неудачная проверка достоверности XML-документа по схеме XSD

Как видите, XML-документ не прошел проверку достоверности. В двух предыдущих примерах для обработки проверки достоверности создавался именованный метод MyValidationEventHandler. Ниже показан тот же самый пример (в частности блок try), но в нем вместо именованного метода для ValidationEventHandler применяется лямбда-выражение:

...
try
{
     xDocument.Validate(schemaSet, (o,vea) =>
     {
             Console.WriteLine("Ошибка проверки достоверности во время обработки объекта " +
                     o.GetType().Name);
             Console.WriteLine(vea.Message);

             throw new Exception(vea.Message);
     });
     Console.WriteLine("Проверка достоверности документа завершена успешно");
}
...

Результат работы данной замены получается аналогичный предыдущему, просто здесь вместо отдельного обработчика используется лямбда-выражение.

Пройди тесты
Лучший чат для C# программистов