Validando XML com Ruby
Publicado em Developer
libxml ruby schema xml xsd
Recebi essa semana um email de um leitor aqui do blog (alô!) que me perguntou como fazer validação de um arquivo XML com Ruby, usando XML Schema. Fui honesto e disse que eu nunca havia precisado usar esse lance em Ruby mas fiquei curioso sobre como fazer, já que algum tempo atrás usei algo similar em PHP. Vamos dar uma olhada em como fazer alguma coisa básica disso.
Antes de mais nada, precisamos dos arquivos. Vou usar esse XML bem simples, que chamei de catalogo.xml:
1 <?xml version="1.0"?> 2 <catalogo loja="Livraria O Quarto Homem" ano="2009"> 3 <categorias> 4 <categoria titulo="ficcao"> 5 <livro> 6 <titulo>A Voz do Fogo</titulo> 7 <autor>Alan Moore</autor> 8 </livro> 9 </categoria> 10 </categorias> 11 </catalogo>
Agora vamos começar a construir o arquivo XSD para a validação, tipo esse que chamei de catalogo.xsd:
1 <?xml version="1.0" encoding="utf-8"?> 2 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> 3 <xs:element name="catalogo"> 4 <xs:complexType> 5 <xs:sequence> 6 <xs:element ref="categorias" minOccurs="1" maxOccurs="unbounded"/> 7 </xs:sequence> 8 <xs:attribute name="loja" type="xs:string"/> 9 <xs:attribute name="ano" type="xs:integer"/> 10 </xs:complexType> 11 </xs:element> 12 13 <xs:element name="categorias"> 14 <xs:complexType> 15 <xs:sequence> 16 <xs:element ref="categoria" minOccurs="1" maxOccurs="unbounded"/> 17 </xs:sequence> 18 </xs:complexType> 19 </xs:element> 20 21 <xs:element name="categoria"> 22 <xs:complexType> 23 <xs:sequence> 24 <xs:element ref="livro" minOccurs="1" maxOccurs="unbounded"/> 25 </xs:sequence> 26 <xs:attribute name="titulo" type="xs:string"/> 27 </xs:complexType> 28 </xs:element> 29 30 <xs:element name="livro"> 31 <xs:complexType> 32 <xs:sequence> 33 <xs:element name="titulo" type="xs:string"/> 34 <xs:element name="autor" type="xs:string"/> 35 </xs:sequence> 36 </xs:complexType> 37 </xs:element> 38 </xs:schema>
Ok, até agora nenhum segredo. Arquivos básicos com validação básica. O lance agora é: como vamos usar Ruby para validar esse XML com esse XSD? Eu escolhi usar a libxml-ruby. Para instalá-la, apenas um
gem install libxml-ruby
e está tudo pronto para começarmos a fazer o nosso código de validação. No site da libxml-ruby há uma documentação ótima, mas vou dar uma resumida na coisa. Criei um arquivo chamado xsd.rb com o seguinte código dentro:
1 require "rubygems" 2 require "xml" 3 4 xml = XML::Document.file(ARGV[0]) 5 schema = XML::Schema.document(XML::Document.file(ARGV[1])) 6 puts xml.validate_schema(schema)
O código fala por si mesmo: criamos um objeto novo para o arquivo XML, outro para o XSD, e pedimos para fazer a validação:
[taq@]$ ruby xsd.rb catalogo.xml catalogo.xsd true
Ok! Tudo funcionando perfeitamente! Para fazer um pequeno teste, vou trocar o 2009 do arquivo XML para, sei lá, "homer", e vamos rodar novamente:
[taq@]$ ruby xsd.rb catalogo.xml catalogo.xsd Error: Element 'catalogo', attribute 'ano': 'homer' is not a valid value of the atomic type 'xs:integer'. at catalogo.xml:2. xsd.rb:6:in `validate_schema': Error: Element 'catalogo', attribute 'ano': 'homer' is not a valid value of the atomic type 'xs:integer'. at catalogo.xml:2. (LibXML::XML::Error) from xsd.rb:6
A-há! Tivemos o comportamento esperado!
Uma das coisas do problema original era utilizar validação de algum elemento usando uma expressão regular. Vamos brincar um pouco com o nome do autor, dizendo que tem que ser composto por dois nomes ou a palavra "Desconhecido". Para isso vamos mudar nosso arquivo XSD para:
1 <?xml version="1.0" encoding="utf-8"?> 2 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> 3 <xs:element name="catalogo"> 4 <xs:complexType> 5 <xs:sequence> 6 <xs:element ref="categorias" minOccurs="1" maxOccurs="unbounded"/> 7 </xs:sequence> 8 <xs:attribute name="loja" type="xs:string"/> 9 <xs:attribute name="ano" type="xs:integer"/> 10 </xs:complexType> 11 </xs:element> 12 13 <xs:element name="categorias"> 14 <xs:complexType> 15 <xs:sequence> 16 <xs:element ref="categoria" minOccurs="1" maxOccurs="unbounded"/> 17 </xs:sequence> 18 </xs:complexType> 19 </xs:element> 20 21 <xs:element name="categoria"> 22 <xs:complexType> 23 <xs:sequence> 24 <xs:element ref="livro" minOccurs="1" maxOccurs="unbounded"/> 25 </xs:sequence> 26 <xs:attribute name="titulo" type="xs:string"/> 27 </xs:complexType> 28 </xs:element> 29 30 <xs:element name="livro"> 31 <xs:complexType> 32 <xs:sequence> 33 <xs:element name="titulo" type="xs:string"/> 34 <xs:element ref="autor" minOccurs="1" maxOccurs="unbounded"/> 35 </xs:sequence> 36 </xs:complexType> 37 </xs:element> 38 39 <xs:element name="autor"> 40 <xs:simpleType> 41 <xs:restriction base="xs:string"> 42 <xs:pattern value="\w+ \w+|Desconhecido"/> 43 </xs:restriction> 44 </xs:simpleType> 45 </xs:element> 46 </xs:schema>
Notem as mudanças nas linhas 34 e da 39 até a 45 e brinquem um pouco com o arquivo XML.
O \w deve funcionar até para todos os caracteres acentuados, mas se por acaso ocorrer algum caso muito específico onde houver a necessidade de deixar explicíta alguma exceção do Unicode, ou você estiver rodando a sua validação em algum lugar onde não tenha controle dos softwares instalados e haja algum problema em relação à libxml com caracteres acentuados, algum problema de encoding dos arquivos, etc. etc.,podemos utilizar alguns padrões bem documentados para contornar isso. Vamos supor que nosso arquivo XML fosse assim:
1 <?xml version="1.0"?> 2 <catalogo loja="Livraria O Quarto Homem" ano="2009"> 3 <categorias> 4 <categoria titulo="ficção"> 5 <livro> 6 <titulo>A Voz do Fogo</titulo> 7 <autor>Alan Moore</autor> 8 </livro> 9 </categoria> 10 <categoria titulo="programação"> 11 <livro> 12 <titulo>Ruby: Conhecendo a Linguagem</titulo> 13 <autor>Eustáquio Rangel</autor> 14 </livro> 15 </categoria> 16 </categorias> 17 </catalogo>
Notem o acento no nome desse quem vos escreve, na linha 13. Agora vamos supor, para efeitos didáticos, que por alguma razão maluca não estamos utilizando o \w na expressão para validar o nome do autor, sei lá, de repente ele não funciona, e sim algo do tipo:
1 <xs:pattern value="[A-z]+ [A-z]+|Desconhecido"/>
O arquivo nunca seria validado:
[taq@]$ ruby xsd.rb catalogo.xml catalogo.xsd Error: Element 'autor': [facet 'pattern'] The value 'Eustáquio Rangel' is not accepted by the pattern '[A-z]+ [A-z]+|Desconhecido'. at catalogo.xml:13. Error: Element 'autor': 'Eustáquio Rangel' is not a valid value of the local atomic type. at catalogo.xml:13. xsd.rb:6:in `validate_schema': Error: Element 'autor': 'Eustáquio Rangel' is not a valid value of the local atomic type. at catalogo.xml:13. (LibXML::XML::Error) from xsd.rb:6
Aí poderíamos ter escrito a expressão dessa maneira:
1 <xs:pattern value="\p{L}+ \p{L}+|Desconhecido"/>
E o arquivo validaria de boa:
[taq@]$ ruby xsd.rb catalogo.xml catalogo.xsd true
O \p{L} ali no caso representa um caracter na categoria "Letra" do Unicode, em qualquer língua. Mais um monte de exemplos de categorias do Unicode vocês podem encontrar aqui e podem ser uma mão na roda para alguma situação mais específica que possa aparecer.
Comentários
Comentários fechados.
Artigos anteriores
- Pull requests em modo raiz - sex, 22 de dezembro de 2023, 09:57:09 -0300
- Qual a idade do seu repositório? - ter, 27 de dezembro de 2022, 12:50:35 -0300
- Utilizando ctags em projetos Rails mais recentes - qui, 24 de junho de 2021, 08:23:43 -0300
- Fazendo o seu projeto brotar - seg, 15 de julho de 2019, 08:57:05 -0300
- Learn Functional Programming with Elixir - sex, 02 de março de 2018, 18:47:13 -0300
- Ambiente mínimo - Driver Driven Development - qua, 23 de agosto de 2017, 15:15:03 -0300
- Ambiente mínimo - repositórios de código - dom, 16 de abril de 2017, 13:02:14 -0300
- Ambiente mínimo - terminal e navegador - dom, 02 de abril de 2017, 21:43:29 -0300
- Utilizando muitas gems no seu projeto? - sáb, 29 de outubro de 2016, 11:57:55 -0200
- Desenvolvedores e inteligência artificial - seg, 11 de julho de 2016, 09:09:38 -0300
Taq teu tuto CRIANDO UMA LOJA.. em ROR fez eu viajar na simplicidade do ROR.. Valeu Mano.
Otimo Post...
VAleu.
Eustaquio, valeu pelo post, o Ruby saiu-se bem na validação XML -> XSD. A Libxml-ruby usa a ultima versao da biblioteca Libxml C. Excelente! Obrigado
Muito bem explicado!
Ainda estou estudando Ruby e consegui compreender e testar seus exemplos sem problemas!
Ruby até agora não me trouxe más surpresas.