XML - Validando um Documento XML com um Schema (C#)


Você já precisou validar um documento XML verificando se ele esta em conformidade com um schema XML ?

Mas onde será que isso pode ser aplicado ?

Se você desejar usar a Nota Fiscal Eletrônica ( NF-e) talvez precise realizar tais validações XML.

Pois hoje vamos mostrar como realizar esta tarefa usando a linguagem C#.

A validação de um documento XML é realizada com a aplicação do respectivo Schema XML, que contêm a definição dos campos e conteúdos válidos para o documento XML a ser validado.

Um esquema XML define as regras que um determinado tipo de documento XML deve seguir. O esquema inclui regras que definem o seguinte:

• Os elementos e atributos que podem aparecer em um documento;
• Os tipos de dados para elementos e atributos;
• A estrutura de um documento, incluindo os elementos que são filhos de outros elementos;
• A ordem e o número de elementos filhos que aparecem em um documento;
• Se os elementos estiverem vazios, podem incluir texto, ou exigir valores fixos;

Em seu nível mais básico, um XML Schema Definition (XSD) define os elementos que podem ocorrer em um documento XML.

Documentos XSD são escritos em XML, e você usa um elemento predefinido separado (chamado <element>) no documento XSD para indicar cada elemento que é necessário no documento XML onde o atributo type indica o tipo de dados.

Aqui está um exemplo para nome e preço de produto:

<xsd:element name="produtoNome" type="xsd:string" />
<xsd:element name="produtoPreco" type="xsd:decimal" />

Os tipos de tipos de dados de esquema são definidos pelo www.w3.org/TR/xmlschema-2 eles são mapeados para os tipos de dados .NET incluindo string, int, long, decimal, float , dataTime, boolean.

Em nosso exemplo tanto o produtoNome como produtoPreco são tipos simples, porque eles contêm somente dados caractere. Elementos que contêm elementos aninhados são chamados de tipos complexos. Você pode aninhá-los em conjunto, utilizando uma tag <sequence>, se a ordem for importante, ou uma tag <all> se não for. A seguir um exemplo de como você pode modelar o elemento <produto> no catálogo de produtos. Observe que os atributos são sempre declarados após os elementos e eles não são agrupados com uma tag <sequence> ou <all> porque a ordem não é importante:

<xsd:complexType name="produto">
   <xsd:sequence>
      <xsd:element name="produtoNome" type="xsd:string"/>
      <xsd:element name="produtoPreco" type="xsd:decimal"/>
      <xsd:element name="emEstoque" type="xsd:boolean"/>
   </xsd:sequence>
<xsd:attribute name="id" type="xsd:integer"/>
</xsd:complexType>

Por padrão, um elemento listado pode ocorrer exatamente uma vez em um documento. Você pode configurar este comportamento especificando os atributos maxOccurs minOccurs. A seguir temos um exemplo que permite um número ilimitado de produtos no catálogo:

<xsd:element name="produto" type="produto" maxOccurs="unbounded" />

Abaixo está o arquivo de esquema para o catalogo de produtos :

<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <!-- Define o tipo complexo produto. -->
  <xsd:complexType name="produto">
    <xsd:sequence>
      <xsd:element name="produtoNome" type="xsd:string"/>
      <xsd:element name="descricao" type="xsd:string"/>
      <xsd:element name="produtoPreco" type="xsd:decimal"/>
      <xsd:element name="emEstoque" type="xsd:boolean"/>
    </xsd:sequence>
    <xsd:attribute name="id" type="xsd:integer"/>
  </xsd:complexType>
  <!-- Esta é a estrutura que o documento precisa estar em conformidade.
       Ela incia com o elemento produtoCatalogo que aninha outros elementos. -->
  <xsd:element name="catalogoProdutos">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element name="catalogoNome" type="xsd:string"/>
        <xsd:element name="dataValidade" type="xsd:date"/>
        <xsd:element name="produtos">
          <xsd:complexType>
            <xsd:sequence>
              <xsd:element name="produto" type="produto"
              maxOccurs="unbounded" />
            </xsd:sequence>
          </xsd:complexType>
        </xsd:element>
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>

A classe XmlReader pode impor estas regras do esquema, permitindo a você solicitar explicitamente uma validação, quando você usar o método XmlReader.Create. (Mesmo que você não usa um leitor de validação, uma exceção será lançada se o leitor descobre que o XML não esta bem formado, como um caráter ilegal,
tags aninhadas indevidamente, etc.).
Assim temos que:

1- ) Quando você chama XmlReader.Create, a chamada de um objeto XmlReaderSettings indica que você deseja executar validação;
2- ) Em seguida, movimentando através do nó do documento um de cada vez pela chamada do XmlReader.Read, e capturando todas as exceções de validação.;
3- ) Para encontrar todos os erros em um documento sem capturar exceções, você pode tratar o evento ValidationEventHandler no objeto XmlReaderSettings dado como parâmetro para XmlReader
;

Depois de ter criado o seu leitor de validação, a validação ocorre automaticamente quando você ler o documento. Assim que um erro for encontrado, o XmlReader gera um evento ValidationEventHandler com informações do erro sobre o objeto XmlReaderSettings dada no momento da criação.

Se você quiser, você pode manipular este evento e continuar processando o documento para encontrar mais erros. Se você não tratar esse evento, uma exceção XmlException será lançada quando o primeiro erro for encontrado, e o processamento será abortado.

A seguir veremos um exemplo para lhe dar um idéia de como podemos fazer a validação de um documento XML. Para isso vou criar uma classe usando a linguagem C# que exibe todos os erros de um documento XML quando o método ValidarXML for chamado.

Os erros serão exibidos em uma janela do console e uma variável boleana será retornada para indicar se a validação ocorreu com sucesso ou não.

Realizando a validação XML

Abra o Visual C# 2010 Express Edition e crie uma aplicação do tipo Console Application com o nome Validando_XML;

No menu Project clique em Add Class e informe o nome da classe como ValidacaoXML.cs. A seguir inclua o código abaixo neste arquivo:

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

namespace Validando_XML
{
    public class ValidacaoXML
    {
        private bool falhou;
        public bool Falhou
        {
            get { return falhou; }
        }

        public bool ValidarXml(string xmlFilename, string schemaFilename)
        {
            // Define o tipo de validação
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.ValidationType = ValidationType.Schema;
            // Carrega o arquivo de esquema
            XmlSchemaSet schemas = new XmlSchemaSet();
            settings.Schemas = schemas;
            // Quando carregar o eschema, especificar o namespace que ele valida
            // e a localização do arquivo 
            schemas.Add(null, schemaFilename);
            // Especifica o tratamento de evento para os erros de validacao
            settings.ValidationEventHandler += ValidationEventHandler;
            // cria um leitor para validação
            XmlReader validator = XmlReader.Create(xmlFilename, settings);
            falhou = false;
            try
            {
                // Faz a leitura de todos os dados XML
                while (validator.Read()) {}
            }
            catch (XmlException err)
            {
                // Um erro ocorre se o documento XML inclui caracteres ilegais
                // ou tags que não estão aninhadas corretamente
                Console.WriteLine("Ocorreu um erro critico durante a validacao XML.");
                Console.WriteLine(err.Message);
                falhou = true;
            }
            finally
            {
                validator.Close();
            }
            return !falhou;
        }

        private void ValidationEventHandler(object sender, ValidationEventArgs args)
        {
            falhou = true;
            // Exibe o erro da validação
            Console.WriteLine("Erros da validação : " + args.Message);
            Console.WriteLine();
        }
    }
}

Agora vamos incluir no projeto o arquivo de esquema XSD com o nome Produtos.xsd que iremos usar para validar o documento XML. No menu Project clique em Add New Item e a seguir selecione o template XML File e a seguir copie o conteúdo do arquivo XML conforme abaixo :

Vamos agora incluir um arquivo XML chamado CatalogoProdutos.xml no projeto. No menu Project clique em Add New Item e a seguir selecione o template XML File e inclua o seguinte código neste arquivo:

Agora vamos usar a classe que criamos para validar o documento XML usando o esquema definido. Para isso eu vou copiar os arquivos .xsd e .xml na pasta c:\dados assim fica mais fácil localizar os arquivos.

No arquivo Program.cs digite o código abaixo que irá realizar a validação do arquivo CatalogoProdutos.xml usando o arquivo de esquema Produtos.xsd:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Validando_XML
{
    class Program
    {
        static void Main(string[] args)
        {
            ValidacaoXML validadorXML = new ValidacaoXML();
            Console.WriteLine("Validando o  arquivo CatalogoProdutos.xml pelo esquema Produtos.xsd.");

            bool ok = validadorXML.ValidarXml(@"C:\dados\CatalogoProdutos.xml",@"c:\dados\Produtos.xsd");
            
            if (!ok)
               Console.WriteLine("A validação falhou.");
            else
               Console.WriteLine("A validação foi realizada com sucesso.");

               Console.ReadLine();
        }
    }
}

No método Main() temos o seguinte:

  1. Criação da instância da classe ValidacaoXML;
  2. Chamada do método ValidarXML dessa classe informando o nome do arquivo xml a validar e o nome do arquivo de esquema usado;

Se nenhum erro for encontrado será exibida somente a mensagem : "A validação foi realizada com sucesso."

Vamos então executar o projeto e verificar o resultado:

 

Vamos agora alterar o arquivo CatalogoProdutos.xml produzindo alguns erros para vermos o resultado obtido. Altere o arquivo conforme abaixo:

<?xml version="1.0" encoding="utf-8" ?>
<catalogoProdutos>
  <catalogoNome>JcmSoft Catalogo 2011</catalogoNome>
  <dataValidade>01 de aneiro de 2012</dataValidade>
  <produtos>
    <produto id="1001">
      <produtoNome>Super DVD NET</produtoNome>
	  <descricao>Material de suporte para Estudo</descricao>
      <produtoPreco>60.00</produtoPreco>
      <emEstoque>true</emEstoque>
    </produto>
    <produto id="1002">
      <produtoNome>Super DVD Video Aulas</produtoNome>
	  <descricao>Video aulas com exemplos praticos </descricao>
      <produtoPreco>50.00</produtoPreco>
      <emEstoque>yes</emEstoque>
    </produto>
    <produto id="1003">
      <produtoNome>Super CD VB</produtoNome>
	  <descricao>Codigos fontes e material de suporte </descricao>
      <produtoPreco>50.00</produtoPreco>
      <Estoque>true</emEstoque>
    </produto>    
  </produtos>
</catalogoProdutos>

Executando novamente a validação iremos obter o seguinte resultado:

Como podemos observar recebemos 3 mensagens de erro correspondendo as alterações que fizemos no arquivo XML para provocar tais erros.

Apenas para mostrar outra maneira de realizar a validação vamos incluir um novo método no arquivo ValidacaoXML.cs com o nome ValidarDocumentoXML conforme o código abaixo:

      public bool ValidarDocumentoXML(string xmlPath, string xsdPath)
        {
            try
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.Schemas.Add(null, xsdPath);
                settings.ValidationType = ValidationType.Schema;
                XmlDocument xmlDocument = new XmlDocument();
                xmlDocument.Load(xmlPath);
                XmlReader xmlReader = XmlReader.Create(new StringReader(xmlDocument.InnerXml), settings);
                while (xmlReader.Read()) { }
                return true;
            }
            catch (Exception ex)
            {
                Console.Write(ex.Message);
                return false;
            }
        }

Vimos assim como realizar a validação de um arquivo XML contra um arquivo de esquema (.XSD) de uma forma bem simples e objetiva.

Pegue o projeto completo aqui : Validando_XML.zip

"Porque já estais mortos, e a vossa vida esta escondida com Cristo em Deus." Colossenses 3:3

Referências:


José Carlos Macoratti