Este tutorial ensina como ler documentos XML através da interface Reader do Xisemele.
Para ler um documento XML você precisa obter uma instância de Reader da seguinte forma:
import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
Reader reader = XisemeleFactory.newXisemele().createReader( documentoXML );
Neste caso, o documentoXML pode ser um java.io.File, um java.io.InputStream ou uma String contendo o documento XML que deverá ser lido.
Você pode referenciar determinados elementos em um documento XML diretamente pelo Path usando o método Reader.find().
Considere o seguinte Exemplo:
import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
String xml = "<raiz>" + "<elementoA>valorA</elementoA>" + "<elementoB>" + "<elementoC>valorC</elementoC>" + "</elementoB>" + "</raiz>"; Reader reader = XisemeleFactory.newXisemele().createReader( xml ); System.out.println( reader.find("raiz/elementoA").name() ); System.out.println( reader.find("raiz/elementoB").name() ); System.out.println( reader.find("raiz/elementoB/elementoC").name() );
O trecho de código acima imprime no console o seguinte resultado:
elementoA elementoB elementoC
O método Reader.find() retorna uma instância de Element correspondente ao Path especificado por parâmetro. O método Element.name(), por sua vez, retorna uma String contendo o nome do elemento referenciado.
O Xisemele usa o conceito de que um elemento XML pode conter valor. Para entender melhor esse conceito leia a seção Valores.
Valores em um documento XML podem ser lidos da seguinte forma:
import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
String xml = "<raiz>" + "<elementoA>10</elementoA>" + "<elementoB>true</elementoB>" + "<elementoC>texto</elementoC>" + "</raiz>"; Reader reader = XisemeleFactory.newXisemele().createReader( xml ); Integer valorA = reader.find("raiz/elementoA").value().asInteger(); Boolean valorB = reader.find("raiz/elementoB").value().asBoolean(); String valorC = reader.find("raiz/elementoC").value().asString();
O método Element.value() retorna uma instância de Value correspondente ao valor do elemento no documento XML. A interface Value contém métodos úteis para conversão do texto de determinado elemento para tipos pré-definidos ou tipos personalizados. A Tabela 1 mostra os métodos da interface Value e os tipos respectivos para qual o valor é convertido.
asByte() | java.lang.Byte |
asShort() | java.lang.Short |
asInteger() | java.lang.Integer |
asLong() | java.lang.Long |
asFloat() | java.lang.Float |
asDouble() | java.lang.Double |
asBigInteger() | java.math.BigInteger |
asBigDecimal() | java.math.BigDecimal |
asBoolean() | java.lang.Boolean |
asString() | java.lang.String |
asDate() | java.util.Date |
asType() | Tipo definido pelo usuário |
O método Value.asType() retorna o valor do elemento convertido para determinado tipo de acordo com a implementação do Formatter configurado. Para entender como configurar um Formatter para um tipo personalizado veja a seção Formatadores.
Os atributos de determinado elemento podem ser lidos da seguinte forma:
import java.util.Date; import net.sf.xisemele.api.Element; import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
String xml = "<raiz>" + "<elemento atr1=\"10\" atr2=\"true\" atr3=\"17/07/2009\" />" + "</raiz>"; Reader reader = XisemeleFactory.newXisemele().createReader( xml ); Element element = reader.find("raiz/elemento"); Integer atr1 = element.attribute("atr1").asInteger(); Boolean atr2 = element.attribute("atr2").asBoolean(); Date atr3 = element.attribute("atr3").asDate("dd/MM/yyyy");
O método Element.attribute() retorna uma instância de Value correspondente ao valor do atributo do qual o nome é especificado por parâmetro. A forma como o valor de um atributo é convertido para determinado tipo é equivalente ao que está definido na Tabela 1.
É possível recuperar a lista de atributos de determinado elemento através do método Element.attributes():
import net.sf.xisemele.api.Attribute; import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
String xml = "<raiz>" + "<elemento atr1=\"10\" atr2=\"true\" atr3=\"17/07/2009\" />" + "</raiz>"; Reader reader = XisemeleFactory.newXisemele().createReader( xml ); for (Attribute atr : reader.find("raiz/elemento").attributes()) { System.out.println( atr.name() + " = " + atr.value().asString() ); }
O método Attribute.name() retorna o nome e o método Attribute.value() retorna uma instância de Value correspondente ao valor do atributo.
O trecho de código acima imprime no console o seguinte resultado:
atr1 = 10 atr2 = true atr3 = 17/07/2009
A interface Element fornece ainda mais 2 métodos para trabalhar com atributos: Element.containsAttributes() e Element.containsAttribute(String). O método Element.containsAttributes() indica se determinado elemento contém atributos e o método Element.containsAttribute(String) indica se o elemento contém o atributo com o nome especificado por parâmetro.
A interface Element fornece métodos que permitem verificar se determinado elemento contém ou não filhos e outros que permitem recuperar os filhos pelo nome, índice ou em uma lista de Element. Os filhos são os elementos que estão delimitados pelas tags que abrem e fecham determinado elemento.
Por exemplo, no seguinte XML:
<?xml version="1.0" encoding="UTF-8"?> <raiz> <elementoA> <elementoB /> <elementoC /> </elementoA> </raiz>
O elemento elementoA é filho da raiz e os elementos elementoB e elementoC são filhos de elementoA.
A existência de elementos filhos pode ser verificada da seguinte forma:
import net.sf.xisemele.api.Element; import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
String xml = "<raiz>" + "<elementoA>" + "<elementoB />" + "<elementoC />" + "</elementoA>" + "</raiz>"; Reader reader = XisemeleFactory.newXisemele().createReader( xml ); Element elementoA = reader.find("raiz/elementoA"); System.out.println( "Contém filhos: " + elementoA.containsChildren() ); System.out.println( "Contém filho com nome elementoB: " + elementoA.containsChild("elementoB") ); System.out.println( "Contém filho com nome elementoC: " + elementoA.containsChild("elementoC") ); System.out.println( "Contém filho com nome elementoD: " + elementoA.containsChild("elementoD") );
O método Element.containsChildren() indica se o elemento contém elementos filhos e o método Element.containsChild(String) indica se o elemento contém algum elemento filho com o nome especificado por parâmetro.
O trecho de código acima imprime no console o seguinte resultado:
Contém filhos: true Contém filho com nome elementoB: true Contém filho com nome elementoC: true Contém filho com nome elementoD: false
A quantidade de elementos filhos pode ser verificada da seguinte forma:
import net.sf.xisemele.api.Element; import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
String xml = "<raiz>" + "<elementoA>" + "<elementoB />" + "<elementoB />" + "<elementoC />" + "<elementoC />" + "<elementoC />" + "</elementoA>" + "</raiz>"; Reader reader = XisemeleFactory.newXisemele().createReader( xml ); Element elementoA = reader.find("raiz/elementoA"); System.out.println( "Quantidade de filhos: " + elementoA.numberOfChildren() ); System.out.println( "Quantidade de filhos com nome elementoB: " + elementoA.numberOfChildren("elementoB") ); System.out.println( "Quantidade de filhos com nome elementoC: " + elementoA.numberOfChildren("elementoC") ); System.out.println( "Quantidade de filhos com nome elementoD: " + elementoA.numberOfChildren("elementoD") );
O método Element.numberOfChildren() retorna a quantidade de elementos filhos e o método Element.numberOfChildren(String) retorna a quantidade de elementos filhos que contém o nome especificado por parâmetro.
O trecho de código acima imprime no console o seguinte resultado:
Quantidade de filhos: 5 Quantidade de filhos com nome elementoB: 2 Quantidade de filhos com nome elementoC: 3 Quantidade de filhos com nome elementoD: 0
Os elementos filhos podem ser acessados da seguinte forma:
import java.util.List; import net.sf.xisemele.api.Element; import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
String xml = "<raiz>" + "<elementoA>" + "<elementoB>valor B</elementoB>" + "<elementoC>valor C:1</elementoC>" + "<elementoC>valor C:2</elementoC>" + "<elementoC>valor C:3</elementoC>" + "</elementoA>" + "</raiz>"; Element elementoA = XisemeleFactory.newXisemele().createReader( xml ).find("raiz/elementoA"); Element elementoB = elementoA.child("elementoB"); // (1) Element elementoC1 = elementoA.child("elementoC"); // (2) Element elementoC3 = elementoA.child(3); // (3) List<Element> filhosDoElementoA = elementoA.children(); // (4) List<Element> elementosC = elementoA.children("elementoC"); // (5)
O método Element.child(String) retorna o primeiro filho encontrado com o nome especificado por parâmetro, como pode ser observado nas declarações (1) e (2). Repare que o elemento elementoA contém três filhos com o mesmo nome elementoC. Não é possível acessar esses três filhos por esse método. Esta é uma limitação que será resolvida em versão futura. Porém, a API fornece o método Element.child(int) que retorna o filho de acordo com o índice especificado por parâmetro. O primeiro elemento filho é indexado pelo número 0 (zero), portanto, a declaração (3) está recuperando o quarto filho, contando de cima para baixo, do elemento elementoA.
Como pode ser observado na declaração (4), o método Element.children() retorna uma lista de Element correspondente aos elementos filhos do elemento elementoA. Se determinado elemento não contiver filhos, a chamada a esse método retornará uma lista vazia. A API fornece ainda o método Element.children(String) que retorna uma lista contendo os elementos filhos com o nome especificado por parâmetro. Na declaração (5) está sendo recuperada uma lista contendo os três elementos filhos de elementoA com o nome elementoC.
Se você leu o tutorial até aqui você deve ter notado que tanto o recurso Path quanto o método Element.child(String) têm limitações para acessar determinados elementos em certas situações. Pode-se dizer que esses dois recursos têm a mesma limitação pois os dois são equivalentes. Ou seja, qualquer elemento em um documento XML que pode ser acessado diretamente através do Path pode também ser acessado através de chamadas aninhadas ao método Element.child(String).
Por exemplo, para o documento XML:
<raiz> <elementoA> <elementoB> <elementoC /> </elementoB> </elementoA> </raiz>
O elemento elementoC pode ser acessado das seguintes formas:
import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
Reader reader = XisemeleFactory.newXisemele().createReader( xml ); reader.find("raiz/elementoA/elementoB/elementoC"); // (1) reader.find("raiz/elementoA/elementoB").child("elementoC"); // (2) reader.find("raiz/elementoA").child("elementoB").child("elementoC"); // (3) reader.find("raiz").child("elementoA").child("elementoB").child("elementoC"); // (4) reader.root().child("elementoA").child("elementoB").child("elementoC"); // (5)
As cinco desclarações acima, de (1) a (5), são equivalentes. Na declaração (5) a chamada reader.root() é equivalente à chamada reader.find("raiz") da declaração (4). O método Reader.root() retorna o elemento raiz do documento XML.
A interface Element fornece dois métodos para recuperar a lista de valores de elementos filhos: Element.childrenValue() e Element.childrenValue(String). Esses métodos podem ser usados da seguinte forma:
import net.sf.xisemele.api.Element; import net.sf.xisemele.api.Reader; import net.sf.xisemele.impl.XisemeleFactory;
String xml = "<raiz>" + "<elementoA>" + "<elementoB>10</elementoB>" + "<elementoB>20</elementoB>" + "<elementoC>30</elementoC>" + "<elementoC>40</elementoC>" + "<elementoC>50</elementoC>" + "<elementoC>60</elementoC>" + "</elementoA>" + "</raiz>"; Reader reader = XisemeleFactory.newXisemele().createReader( xml ); Element elementoA = reader.find("raiz/elementoA"); System.out.println( elementoA.childrenValue().asString() ); System.out.println( elementoA.childrenValue("elementoB").asInteger() ); System.out.println( elementoA.childrenValue("elementoC").asDouble() );
O método Element.childrenValue() retorna uma instância de ListValue que abstrai a lista de valores de todos os elementos filhos. O método Element.childrenValue(String) retorna uma instância de ListValue que abstrai a lista de valores dos filhos com o nome especificado por parâmetro.
A interface ListValue é semelhante à interface Value demonstrada na Tabela 1. A única diferença é que cada método da interface Value retorna um tipo X correspondente ao método asX() enquanto que o mesmo método na interface ListValue retorna uma instância de java.util.List contendo valores desse tipo.
O trecho de código acima imprime no console o seguinte resultado:
[10, 20, 30, 40, 50, 60] [10, 20] [30.0, 40.0, 50.0, 60.0]