14 de abril de 2009

Validando um XML contra seu esquema XSD

No dia a dia do profissional de TI, cada vez mais precisamos trabalhar com dados no formato XML. Vira e mexe, é preciso ainda saber se um XML está bem formado, isto é, se ele atende os requisitos dispostos num arquivo de esquema XSD. Um exemplo bem recente disso são os XMLs para envio à Receita Federal no âmbito da Escrituração Digital (SPED) e da Nota Fiscal Eletrônica, onde cada XML tem que seguir regras de formação bem definidas pela Receita, através de arquivos XSD. Os esquemas para NFe podem ser encontrados aqui.

Pesquisando no Google, encontrei uma porção de sites capazes de fazer a validação on-line e também ferramentas - algumas gratuitas, outras pagas. No caso da NFe, a própria Receita Federal faz essa validação mas é preciso submeter ao Web Service deles e aguardar a resposta assíncrona.

Como não encontrei nada que eu achasse prático, resolvi montar um validador por minha conta, para ter à mão sempre que necessário. Pra minha surpresa, descobri que o framework .Net já possui classes para validar um XML e que o uso delas é bastante simples.

Mãos à obra ! Montei uma aplicação simples em C# no Visual Studio 2008 : tem 3 caixas de edição e um botão, como mostra o print screen abaixo:

Ao clicar o botão, o arquivo indicado na caixa Caminho do XML é carregado através de um Reader previamente configurado para realizar a validação enquanto faz a carga. Abaixo, o código comentado do evento de clique no botão :
/* XmlReaderSettings é a configuração de como o XML deve ser lido */
XmlReaderSettings settings = new XmlReaderSettings();
FileStream fs = null;

/* Informa qual é o namespace (se houver) e o caminho do esquema XSD que será usado p/ validar o XML durante a leitura */
settings.Schemas.Add (edNamespace.Text, edXSD.Text);

/* Se dentro do XSD houver referência a outros XSDs externos, pode ser necessário ter certas permissões para localizá-lo. Usa um "Resolver" com as credencias-padrão para obter esse recurso externo. */
XmlUrlResolver resolver = new XmlUrlResolver();
resolver.Credentials = CredentialCache.DefaultCredentials;

/* Informa à configuração de leitura do XML que deve usar o "Resolver" criado acima e que a validação deve respeitar o esquema informado no início. */
settings.XmlResolver = resolver;
settings.ValidationType = ValidationType.Schema;

try {
/* Abre o arquivo XML usando o caminho que foi informado na tela. */
fs = new FileStream(edXML.Text, FileMode.Open,
FileAccess.Read);

/* Constrói o leitor de XML, passando o arquivo XML que foi aberto e as configurações que sinalizam como o leitor deve trabalhar. */
XmlReader reader = XmlReader.Create (fs, settings);

/* Constrói um documento XML vazio */
XmlDocument xmlDoc = new XmlDocument();

/* Manda carregar as informações nesse XML através do leitor montado acima. */
xmlDoc.Load(reader);

/* Só chega aqui se carregou tudo OK. */
MessageBox.Show(this, "Validação concluída com sucesso.",
"Aviso", MessageBoxButtons.OK,
MessageBoxIcon.Information);

} catch (Exception exc)
{
/* Se houver erro na validação do XML, uma exceção é levantada e o fluxo vem para esse ponto, exibindo a mensagem de erro.*/
MessageBox.Show(this, exc.Message, "Erro",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}

/* Fecha o stream para liberar o arquivo. */
if (fs != null)
{
fs.Close();
fs.Dispose();
}
Ao final, se foi tudo OK com a carga, tem-se na instância xmlDoc a árvore do XML montada, validada e pronta para ser usada pelo programa.

Obs: No lugar de ler o XML de um arquivo, pode-se usar qualquer tipo de Stream - até mesmo lido da memória (MemoryStream).

Clique aqui para fazer o download do programa. Inclui aqui no blog também alguns posts sobre a construção de arquivos XSD para facilitar o entendimento de como isso funciona.

30 comentários :

Geovane Velmer disse...

Poste o link para dowload desse programa

Luís Gustavo Fabbro disse...

Chris

Obrigado pelo seu feedback ! Inclui no post um link para download do programa do exemplo.

[]s

Anônimo disse...

Uma duvida esse validador identica a má formação do XML, eu pergunto porque tive uma resposta da receita dessa forma "má formação do XML" em ambiente Scan.

Meu email: analista.edilson@hotmail.com

Luís Gustavo Fabbro disse...

A ideia do programa é justamente verificar se o XML está bem formado, isto é, se ele atende às regras de formação estipuladas no XSD (esquema). Então, a resposta é "sim", ele será capaz de apontar as má formações estruturais apresentadas pelo seu XML.

No entanto, ele não conseguirá identificar má formação semântica, isto é, relativa aos cruzamentos que a Receita Federal faz com alguns campos. Por exemplo, se a inscrição estadual informada é válida ou não; se o Estado ou Município do Emissor que foi informado na NFe está diferente daquele registrado na própria Receita; etc..

Anônimo disse...

Gustavo,

Vejo que no exemplo disponível para download tem como pegar todos os erros e apenas o primeiro. Como você fez isso?

Luís Gustavo Fabbro disse...

No código listado no post, o .NET gera uma exceção quando o primeiro erro é encontrado, interrompendo imediatamente a validação.

Para obter todos os erros de uma vez, usei o evento ValidationEventHandler existente no XmlReaderSettings. Para usá-lo, crie uma nova instância da classe ValidationEventHandler e a atribuí à propriedade de mesmo nome no settings. O construtor dessa classe recebe como parâmetro uma função para onde as exceções relativas à validação são redirecionadas.

Assim, basta concatenar as mensagens de erro recebidas por essa função e exibí-las ao fim da validação.

Kaichiro Fukuda disse...

Opa, boa tarde a todos.

Não compreendi muito bem a parte:

"Para obter todos os erros de uma vez, usei o evento ValidationEventHandler existente no XmlReaderSettings. Para usá-lo, crie uma nova instância da classe ValidationEventHandler e a atribuí à propriedade de mesmo nome no settings. O construtor dessa classe recebe como parâmetro uma função para onde as exceções relativas à validação são redirecionadas" !?!

Luís Gustavo Fabbro disse...

Kaichiro

A versão do código C# para validação que está reproduzida no post interrompe sua execução ao encontrar um erro; outros erros que eventualmente existam no XML só serão revelados depois que esse primeiro erro for corrigido.

Assim, se quiser capturar todos os erros do XML de uma única vez, responda ao evento ValidationEventHandler existente no XmlReaderSettings. A cada erro encontrado, o evento é disparado uma vez, dando a oportunidade de você salvar as mensagens para exibição.

Veja a documentação dele no MSDN : evento ValidationEventHandler

Unknown disse...

Opa, vlw. Deu certo agora.

Anônimo disse...

Olá Luis, posso usar seu programa como um fiel da balança para um cliente, explico.
Tenho que gerar vários xml para esse cliente, meu programa gera e valida, mas eu gostaria de um programa de terceiros para ficar como um "tira-teima", posso usar seu programa para isso?? tem algum custo??

Luís Gustavo Fabbro disse...

Pode usá-lo, sem problemas. Ele não tem custo, até porque o código que faz a validação está todo reproduzido no post. Isto também te deixa livre para criar sua própria versão do validador, implementando outras funcionalidades que julgar necessárias.

rapozao157 disse...

Luis, estou tentando fazer a validação e está dando um erro 'http://www.w3.org/2000/09/xmldsig#:Signature' não foi declarado.

Voce tem msn facil?

Luís Gustavo Fabbro disse...

Rapozao (?)

É bem provável que o XML que você passou não possui a tag Signature exigida pelo esquema XSD.

O valor http://www.w3.org/2000/09/xmldsig# é o namespace onde a tag Signature deve ser inserida. Lembre-se de que, se um namespace não for especificado, a tag é considerada no escopo do namespace do nó-pai onde ela está inserida.

No caso da NFe, por exemplo, você deve declarar explicitamente o Signature como parte do http://www.w3.org/2000/09/xmldsig#, senão o namespace considerado será http://www.portalfiscal.inf.br/nfe, o que resultará em erro quando o XML for confrontado com o XSD.

Aqui no blog tem um post sobre assinatura digital de arquivos XML que pode te ajudar :
Incluindo a assinatura digital no XML.

Anônimo disse...

tem como mandar o fonte do exemplo nao ? ou melhor ainda deixa o fonte disponivel no blog.

se possivel por email (tbalbinos@oi.com.br)

obrigado

Parabéns !! Thiago B.

Luís Gustavo Fabbro disse...

Thiago

O código completo para a validação está listado no corpo do post e inclui comentários sobre o funcionamento de cada parte. Insira esse código como resposta a um evento - como o clique de um botão "Validação" num form, por exemplo.

[]s

Anônimo disse...

não achei o post ccom o link para baixar o fonte do programa e gostaria de receber, sera que pode me mandar no e-mail, o meu email é tom.camara@gmail.com, desde já agradeço

Luís Gustavo Fabbro disse...

Tom

Como eu disse no comentário anterior, o código está todo no post. Se está com dificuldade de incluí-lo como resposta a um evento, poste sua dúvida (ou envie no email do blog).

Anônimo disse...

Amigo, obrigado pelo programa e pelo código.

Ficou faltando apenas dar um Close no Stream depois da validação. Peguei seu programa para validar o XML que o meu sistema estava gerando, e após cada validação, preciso fechar seu aplicativo, gerar o novo XML, abrir seu aplicativo e digitar novamente o schema, caminho do XSD e do XML.

Claro que pra facilitar meu trabalho, já gerei a minha própria versão corrigida, mas vale você corrigir o seu também!

Luís Gustavo Fabbro disse...

Obrigado pelo aviso ! Já tinha feito essa alteração na minha versão local mas não tinha atualizado o post.

Agora o encerramento do stream está no código do post e subi de novo o arquivo pra download.

[]s

Dirso! disse...

Muito bom seu post... porém gostaria de sabe se caso vc possui os fontes, teria como disponibilizar?
Pois o aplicativo que tem pra download, tem um segundo botão no qual "valida tudo".

Desde já muito obrigado!

PS: meu email é edilsonld@gmail.com

Luís Gustavo Fabbro disse...

Edilson

Como eu disse em outro comentário, se quiser capturar todos os erros do XML de uma única vez, você terá que responder ao evento ValidationEventHandler existente no XmlReaderSettings. A cada erro encontrado, o evento é disparado uma vez, dando a oportunidade de você salvar as mensagens para exibição.

Veja a documentação dele no MSDN : evento ValidationEventHandler.

Dirso! disse...

Muito obrigado!
Funcionou o que queria fazer.

LANDD disse...

Elemento 'http://www.portalfiscal.inf.br/nfe:nfeProc' não foi declarado.

CARA QUANDO VOU VALIDAR DA ESSE ERRO OQUE PODE SER ??

cassio.infro@gmail.com

Luís Gustavo Fabbro disse...

Cássio

O XML que você está testando não bate com a estrutura definida no esquema XSD. A definição do XSD exige a presença da tag nfeProc mas o XML não tem uma tag com esse nome - ao menos não no lugar esperado, que me parece ser a raiz. Veja se a tag existe e se está declarada no namespace correto (http://www.portalfiscal.inf.br/nfe).

[]s

Unknown disse...

No XSD não tem nfeProc na estrutura. Já o XML sim, e na hora de validar da erro "Elemento 'http://www.portalfiscal.inf.br/nfe:nfeProc' não foi declarado". Alguem pode ajudar?

Luís Gustavo Fabbro disse...

Leonardo

Os XSD da Receita Federal costumam trabalhar com include, aninhando um XSD em outro através da referência a outro arquivo. É nesse outro arquivo que deve estar a definição do nfeProc.

Você diz que seu XML possui a tag criticada como inexistente; é bem provável que ela não esteja declarada no namespace http://www.portalfiscal.inf.br/nfe, indicado no erro.

[]s

E hoje o que vai ser disse...

Ola, baixei o aplicativo, para tentar validar um CC-e de CT-e mas não tive sucesso, sei que o aplicativo não é para isto (srs)
Gostaria de saber se alguém teve problemas com formação do XML para CC-e de CT-e pois não encontro muito material referente a isso, comparei com outros e esta aparentemente tudo certo.!

Luís Gustavo Fabbro disse...

Luis

A aplicação publicada no post é capaz de validar o XML do CCe, comparando o conteúdo dele com o previsto no respectivo XSD (esquema). Se o programa acusou um erro é porque o XML não está em pleno acordo com as regras determinadas pelo XSD. Baseado no erro reportado, vc pode modificar seu XML e submetê-lo à aplicação novamente pra certificar que não há mais nada errado com ele.

Obs: O programa não é capaz de submeter seu XML a um web service para registrar oficialmente a transação; para isso, há outros posts no blog que podem ser úteis.

[]s

Garibaldo Guerreiro disse...

Luis, boa noite. Baixei a sua e instalei em meu sistema, porém, na compilação está dando um erro nessa linha:
resolver.Credentials = CredentialCache.DefaultCredentials;
o erro é esse:
Error 3 'string' does not contain a definition for 'DefaultCredentials' and no extension method 'DefaultCredentials' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?)
O que eu posso está deixando de fazer? Sou novo no C# .

Luís Gustavo Fabbro disse...

Garibaldo

A mensagem de erro dá a entender que há uma variável declarada em seu programa com o nome de CredentialCache. O post, no entanto, faz referência à classe existente em System.Net, bastando incluir a cláusula using System.Net; no início do seu código. A documentação da classe está aqui.

[]s

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.