Android - Web Services - XML
Transcript of Android - Web Services - XML
Software Engineer: Israel Rosas Soria
Twitter:@ssnova24
Correo: [email protected]
Android Web Services (Tratamiento de XML)
Tratamiento XML
1. Tratamiento de XML con SAX.
2. Tratamiento de XML con SAX Simplificado.
3. Tratamiento de XML con DOM.
4. Tratamiento de XML con XmlPull.
Para el manejo exclusivo de XML, tendremos 4 maneras de tratarlos, los cuales
los mencionamos a continuación:
Tratamiento XML
Como podemos observar el xml se compone de un elemento principal <channel> y
posteriormente una lista de elementos <item> para cada noticia con sus datos
asociados.
Para poder trabajar, procederemos a crear nuestro modelo; llamado “Noticia”, al cual le
colocaremos los siguiente atributos, y generaremos sus métodos de acceso.
private String titulo; private String link; private String descripcion; private String guid; private String fecha;
(*) Definiremos a todos los
métodos como String para no
complicarnos en el desarrollo del
laboratorio.
Ahora, que ya sabemos con que estructura y que clase auxiliar vamos a trabajar
procederemos a detallar cada modelo, cualquiera de los mencionados anteriormente
pueden tratar XML de manera online como local, pero dependiendo del contexto
veremos cual resulta mas eficiente para ciertos escenarios. Como estamos viendo
Servicios Web, nos centraremos en xml Online.
Utilizaremos el archivo llamado “primerXml.xml”, de nuestra carpeta XMLS
Tratamiento XML
1. Tratamiento de XML con SAX.
2. Tratamiento de XML con SAX Simplificado.
3. Tratamiento de XML con DOM.
4. Tratamiento de XML con XmlPull.
Puntos a seguir, para el tratamiento XML:
Tratamiento XML - SAX
SAX (Simple API for XML)
El tratamiento se basa en un analizador (parser), que lee secuencialmente el documento y
a su vez genera eventos con la información de cada elemento.
Entonces, por ejemplo:
<title> -> startElement() Texto dentro de la etiqueta -> characters()
Los métodos principales son: startDocument(): comienza el documento XML. endDocument(): termina el documento XML. startElement(): comienza una etiqueta XML. endElement(): termina una etiqueta XML. characters(): fragmento de texto.
Lista Completa: http://developer.android.com/reference/org/xml/sax/helpers/DefaultHandler.html
Tratamiento XML - SAX
Para poder hacer uno de los métodos mencionados debemos extender de la clase
DefaultHandler, ahora crearemos una clase llamada “NoticiaHandler” que extienda de
DefaultHandler.
A dicha clase declararemos los siguientes atributos:
private List<Noticia> noticias; private Noticia noticiaActual; private StringBuilder sbTexto;
Generaremos el método de acceso
get, para la variable tipo List
Vamos a generar un Listado de Noticias, y con el método getNoticias() [Lo
realizaremos con el método de acceso para get()] obtendremos las noticias tras el
termino de lectura del documento.
Ahora comencemos con los eventos SAX necesarios.
Tratamiento XML - SAX
Comencemos por startDocument(), este evento indica que se ha comenzado a
leer el documento XML, por lo que lo aprovecharemos para inicializar la lista de
noticias y las variables auxiliares.
noticias = new ArrayList<Noticia>(); sbTexto = new StringBuilder();
Ahora procederemos con el evento startElement() se lanza cada vez que se
encuentra una nueva etiqueta de apertura. En nuestro caso, la única
etiqueta que nos interesará será <item>, momento en el que inicializaremos
un nuevo objeto auxiliar de tipo Noticia donde almacenaremos
posteriormente los datos de la noticia actual.
if (localName.equals("item")) { noticiaActual = new Noticia(); }
Tratamiento XML - SAX
El siguiente evento relevante es characters(), que se lanza cada vez que se encuentra un
fragmento de texto en el interior de una etiqueta. La técnica aquí será ir acumulando en
una variable auxiliar, sbTexto, todos los fragmentos de texto que encontremos hasta
detectarse una etiqueta de cierre.
if (this.noticiaActual != null) {
sbTexto.append(ch, start, length); }
Por último, en el evento endElement(), lo que haremos será almacenar en el objeto
noticiaActual (que conoceremos por el parámetro localName devuelto por el evento) el
texto que hemos ido acumulando en la variable sbTexto y limpiaremos dicha variable para
comenzar a acumular el siguiente dato.
El único caso especial será cuando detectemos el cierre de la etiqueta <item>, que
significará que hemos terminado de leer todos los datos de la noticia y por tanto
aprovecharemos para añadir la noticia actual a la lista de noticias que estamos
construyendo.
Tratamiento XML - SAX
if (this.noticiaActual != null) { if (localName.equals("title")) { noticiaActual.setTitulo(sbTexto.toString()); } else if (localName.equals("link")) { noticiaActual.setLink(sbTexto.toString()); } else if (localName.equals("description")) { noticiaActual.setDescripcion(sbTexto.toString()); } else if (localName.equals("guid")) { noticiaActual.setGuid(sbTexto.toString()); } else if (localName.equals("pubDate")) { noticiaActual.setFecha(sbTexto.toString()); } else if (localName.equals("item")) { noticias.add(noticiaActual); } sbTexto.setLength(0);
}
Tratamiento XML - SAX
Ahora crearemos una clase que nos ayude a realizar el Parser, la llamaremos
NoticiaParserSax.
Esta clase tendrá únicamente un constructor que reciba como parámetro la URL del
documento a parsear, y un método público llamado parse() para ejecutar la lectura del
documento, y que devolverá como resultado una lista de noticias.
private URL noticiaUrl; Parámetro URL:
public NoticiaParserSax(String url){ try{ this.noticiaUrl = new URL(url); }catch (MalformedURLException e){ throw new RuntimeException(e); }
}
Constructor
El constructor de la clase se limitará a aceptar como parámetro la URL del documento XML a parsear a controlar la validez de dicha URL, generando una excepción en caso contrario.
Tratamiento XML - SAX
public List<Noticia> parse(){ SAXParserFactory factory = SAXParserFactory.newInstance(); try{
SAXParser parser = factory.newSAXParser(); NoticiasHandler handler = new NoticiasHandler(); parser.parse(this.getInputStream(), handler); return handler.getNoticias();
}catch (Exception e){ throw new RuntimeException(e); }
}
Método público
parse()
Será el encargado de crear un nuevo parser SAX mediante su fábrica correspondiente y de iniciar el proceso pasando al parser una instancia del handler que hemos creado anteriormente y una referencia al documento a parsear en forma de stream.
Tratamiento XML - SAX
Ahora solamente en nuestro Activity nos falta enviarle el url al momento de crear un nuevo
ParserSax; y no nos olvidemos del permiso hacia internet.
NoticiaParserSax saxparser = new NoticiaParserSax("http://www.e-linguasac.com/primerXml.xml"); List<Noticia> noticias = saxparser.parse();
Método
getInputStream()
private InputStream getInputStream(){ try{ return noticiaUrl.openConnection().getInputStream(); }catch (IOException e){ throw new RuntimeException(e); }
}
Se encarga de abrir la conexión con la URL especificada mediante openConnection() y obtener el stream de entrada
<uses-permission android:name="android.permission.INTERNET" />
Tratamiento XML
1. Tratamiento de XML con SAX.
2. Tratamiento de XML con SAX Simplificado.
3. Tratamiento de XML con DOM.
4. Tratamiento de XML con XmlPull.
Puntos a seguir, para el tratamiento XML:
Tratamiento XML – SAX
Simplificado
En las diapositivas anteriores vimos como consumir un XML con SAX Clásico, vimos como
utilizar un handler SAX , donde se definían las acciones a realizar tras recibirse cada uno
de los posibles eventos generados por el parser XML.
Desventajas del SAX Clásico:
Definir una clase independiente para el handler.
Los eventos SAX definidos no están ligados de ninguna forma a etiquetas concretas del
documento XML sino que se lanzarán para todas ellas.
Tener que colocar condicionales en el método endElement(), En primer lugar teníamos
que comprobar la condición de que el atributo noticiaActual no fuera null, para evitar
confundir el elemento <title> descendiente de <channel> con el del mismo nombre pero
descendiente de <item>.
Si esto no se valida correctamente para un documento un poco mas complejo, se puede
complicar, y generar errores.
Tratamiento XML – SAX
Simplificado
Android propone una variante del modelo SAX que evita definir una clase separada para el
handler y que permite asociar directamente las acciones a etiquetas concretas dentro de
la estructura del documento XML, lo que alivia en gran medida los inconvenientes
mencionados.
Ahora generaremos una clase llamada “NoticiasParserSax2”, en ella declararemos 2
atributos:
private URL noticiaUrl; private Noticia noticiaActual;
try{ this. noticiaUrl = new URL(url); } catch (MalformedURLException e){ throw new RuntimeException(e); }
En el constructor por defecto,
Recibiremos una url para
validar:
Tratamiento XML – SAX
Simplificado
Ahora en nuestro método parser(), ya no declararemos un parser factory, sino esta vez
especificaremos directamente a la raíz y los elementos.
public List<Noticia> parse(){ final List<Noticia> noticias = new ArrayList<Noticia>(); RootElement root = new RootElement("rss"); Element channel = root.getChild("channel"); Element item = channel.getChild("item"); //Aquí comenzaremos a leer el documento en si.
}
Tratamiento XML – SAX
Simplificado Para inicializar la lectura del documento, procedemos a realizar
setStartElementListener(), y luego procedemos a inicializar el atributo noticiaActual
item.setStartElementListener(new StartElementListener(){ public void start(Attributes attrs) { noticiaActual = new Noticia(); }
});
Para finalizar la lectura del documento, procedemos a realizar setEndElementListener(),
y luego procedemos a agregar a la lista “noticias”, los objetos noticiaActual.
item.setEndElementListener(new EndElementListener(){ public void end() { noticias.add(noticiaActual); }
});
Tratamiento XML – SAX
Simplificado Para poder extraer los elementos de nuestro archivo xml, procedemos a hacer uso del
método: item.getChild(“nombre del elemento").setEndTextElementListener(), para
nuestro ejemplo vamos a tener 5 elementos: title, link, description, guid, pubDate.
item.getChild("title").setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { noticiaActual.setTitulo(body); } }); item.getChild("link").setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { noticiaActual.setLink(body); } });
Tratamiento XML – SAX
Simplificado
item.getChild("description").setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { noticiaActual.setDescripcion(body); } }); item.getChild("guid").setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { noticiaActual.setGuid(body); } }); item.getChild("pubDate").setEndTextElementListener(new EndTextElementListener(){ public void end(String body) { noticiaActual.setFecha(body); } });
Tratamiento XML – SAX
Simplificado Antes de terminar realizaremos el parser, con ayuda de la clase “Xml”, que nos permitirá
establecer un Stream para la lectura del xml; el método parser recibirá 3 parámetros: la
conexión para el acceso con el Stream, el tipo de codificación y finalmente
try{ Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler()); } catch (Exception ex){ throw new RuntimeException(ex); }
Una vez realizado procederemos a retornar el listado de noticias.; Como vemos nos pedirá
realizar un método llamado getInputStream(), el cual nos devolverá la conexión para poder
entrar a la estructura del xml.
Tratamiento XML – SAX
Simplificado Método getInputStream():
private InputStream getInputStream(){ try{ return noticiaUrl.openConnection().getInputStream(); } catch (IOException e){ throw new RuntimeException(e); }
}
Como ya sabemos este método es el que nos permitirá crear el acceso o la entrada al Stream.
Tratamiento XML – SAX
Simplificado - Importante Puede que en ocasiones el servicio de internet, se corte y después de un momento
regrese, cuando esto sucede, la aplicación puede que pierda el hilo del proceso, cuando
esto sucede, puede que genere error de conexión, para evitar estos problemas futuros
vamos a definir una política de hilo en modo estricto, de manera que la app, mantenga el
hilo en uso.
if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); }
Colocar esta validación en el método onCreate(), del Activity
Tratamiento XML
1. Tratamiento de XML con SAX.
2. Tratamiento de XML con SAX Simplificado.
3. Tratamiento de XML con DOM.
4. Tratamiento de XML con XmlPull.
Puntos a seguir, para el tratamiento XML:
Tratamiento XML – DOM
Los dos modelos vistos anteriormente nos realizaban una lectura del xml de manera
secuencial, por otro lado con DOM, el documento XML se lee completamente antes de
poder realizar ninguna acción en función de su contenido.
El parser DOM devuelve todo su contenido en forma de una estructura de tipo árbol,
donde vemos la jerarquía padre-hijo.
<noticias> <noticia>
<titulo>T1</titulo> <link>L1</link>
</noticia> <noticia>
<titulo>T2</titulo> <link>L2</link>
</noticia> <noticias>
Como vemos, este árbol conserva la misma información contenida en el fichero XML pero en forma de nodos y transiciones entre nodos, de forma que se puede navegar fácilmente por la estructura.
Tratamiento XML – DOM Ventajas Sustanciales:
Este árbol se conserva persistente en memoria una vez leído el documento completo.
A diferencia de SAX podemos procesarlo en cualquier orden y tantas veces como sea
necesario.
Ahora comenzaremos a realizar el parser mediante DOM para poder realizar la
comparativa con SAX. Para esto crearemos la clase “NoticiaParseDom”.
Como hasta el momento hemos visto siempre manejamos una ruta, en este caso también
tendremos que definirla.
private URL noticiaUrl; Parámetro URL:
public NoticiaParserDom(String url){ try{ this.noticiaUrl = new URL(url); }catch (MalformedURLException e){ throw new RuntimeException(e); }
}
Constructor
Tratamiento XML – DOM
Como ya conocemos definiremos nuestro método parser(), donde instanciaremos de la
clase DocumentBuilderFactory.
public List<Noticia> parse(){ //Instanciamos la fábrica para DOM DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); List<Noticia> noticias = new ArrayList<Noticia>(); //Aquí comenzaremos a leer el documento en si.
}
Simplemente instanciamos para crear nuestro factory con el cual utilizaremos para crear nuestro parser.
Tratamiento XML – DOM
Ahora procederemos a crear nuestro parser
try{ //Creamos un nuevo parser DOM DocumentBuilder builder = factory.newDocumentBuilder(); //Realizamos la lectura completa del XML Document dom = builder.parse(this.getInputStream()); //Nos posicionamos en el nodo principal del árbol (<rss>) Element root = dom.getDocumentElement(); //Localizamos todos los elementos <item> NodeList items = root.getElementsByTagName("item"); //AQUÍ CONTINUAREMOS REALIZANDO LA LECTURA
}catch (Exception ex){ throw new RuntimeException(ex); } return noticias;
Tratamiento XML – DOM
Comenzaremos a leer la lista de las noticias (items)
//Recorremos la lista de noticias for (int i=0; i<items.getLength(); i++){
Noticia noticia = new Noticia(); //Obtenemos la noticia actual Node item = items.item(i); //Obtenemos la lista de datos de la noticia actual NodeList datosNoticia = item.getChildNodes(); }
Sabemos que items, es el que va almacenar las noticias del xml, entonces procedemos a recorrerlo.
Hay que tener presente que NodeList, reconoce todos los elementos que le coloquemos y los almacena en memoria para su posterior utilización.
Tratamiento XML – DOM Una vez, recorrido todas las noticias procedemos a obtener el contenido de cada noticia y
la asignamos a nuestro modelo, finalmente el modelo lo agregaremos a la lista.
for (int j=0; j<datosNoticia.getLength(); j++){ Node dato = datosNoticia.item(j); String etiqueta = dato.getNodeName(); if (etiqueta.equals("title")){
String texto = obtenerTexto(dato); noticia.setTitulo(texto);
}else if (etiqueta.equals("link")){ noticia.setLink(dato.getFirstChild().getNodeValue()); }else if (etiqueta.equals("description")){
String texto = obtenerTexto(dato); noticia.setDescripcion(texto);
}else if (etiqueta.equals("guid")){ noticia.setGuid(dato.getFirstChild().getNodeValue()); }else if (etiqueta.equals("pubDate")){ noticia.setFecha(dato.getFirstChild().getNodeValue()); }
} noticias.add(noticia);
Tratamiento XML – DOM Vemos 2 casos especiales:
if (etiqueta.equals("title")){ String texto = obtenerTexto(dato); noticia.setTitulo(texto);
}else if (etiqueta.equals("description")){ String texto = obtenerTexto(dato); noticia.setDescripcion(texto);
}
Como vimos en el ejemplo gráfico de árbol DOM, el texto de un nodo determinado se
almacena como nodo hijo. Este nodo de texto suele ser único, por lo que la forma
habitual de obtener el texto de un nodo es obtener su primer nodo hijo y obtener su
valor:
String texto = nodo.getFirstChild().getNodeValue();
Tratamiento XML – DOM
private String obtenerTexto(Node dato){ StringBuilder texto = new StringBuilder(); NodeList fragmentos = dato.getChildNodes(); for (int k=0;k<fragmentos.getLength();k++){ texto.append(fragmentos.item(k).getNodeValue()); } return texto.toString();
}
Pero en ocasiones el texto del nodo viene fragmentado en varios nodos hijos, esto
sucede porque se utilizan entidades HTML; por ejemplo “"” (“) . En este caso hay
que recorrer todos los nodos hijos e ir concatenando para obtener el texto completo.
En nuestro método de obtenerTexto vamos a enviar el Nodo del cual vamos a comenzar a concatenar y realizamos el mismo procedimiento como si estuviéramos leyendo cualquier NodeList.
Finalmente instanciaremos a nuestra clase NoticiaParserDom desde el Activity Main, y ya tenemos el tratamiento XML con DOM.
Tratamiento XML
1. Tratamiento de XML con SAX.
2. Tratamiento de XML con SAX Simplificado.
3. Tratamiento de XML con DOM.
4. Tratamiento de XML con XmlPull.
Puntos a seguir, para el tratamiento XML:
Tratamiento XML – XmlPull
Ahora vamos a ver el último método, el cual es una versión similar al modelo StAX
(Streaming API for XML), que en esencia es muy parecido al modelo SAX ya comentado.
Entonces en donde radica la diferencia??
En el SAX no podiamos intervenir una vez iniciada la lectura secuencial del XML, en
cambio en XmlPull podremos indicarle de forma explicita la lectura del siguiente elemento
del XML.
Crearemos nuestra clase “NoticiaParserPull”, en la cual como ya sabemos crearemos
nuestro método de validación de URL.
En nuestro método parser, definiremos estos 2 atributos, y los inicializarlos
List<Noticia> noticias = null; XmlPullParser parser = Xml.newPullParser();
Creamos nuestro try catch que venimos utilizando y retornamos la lista de noticias.
Tratamiento XML – XmlPull
Comenzaremos a definir los siguientes atributos, donde el getInputStream como ya
sabemos establece la conexión, el evento nos generara un tipo e instanciamos un objeto
de tipo noticia:
parser.setInput(this.getInputStream(), null); int evento = parser.getEventType(); Noticia noticiaActual = null;
Nos centraremos principalmente en el evento, el cual nos indicará si estamos iniciando el
documento, también el inicio de los tag y demás.
Comenzaremos con crear un while, con dicha estructura vamos a recorrer el documento.
while (evento != XmlPullParser.END_DOCUMENT){ String etiqueta = null; //comenzaremos a recorrer las etiquetas evento = parser.next(); }
Tratamiento XML – XmlPull
Mientras el evento sea diferente a Fin del Documento. Generaremos un switch para los
demás casos.
switch (evento) { //cases…. }
En primer lugar veremos si estamos posicionándonos en el inicio del documento:
case XmlPullParser.START_DOCUMENT: noticias = new ArrayList<Noticia>(); break;
Ahora procederemos a leer los tag:
case XmlPullParser.START_TAG: etiqueta = parser.getName(); //ANALIZAREMOS LAS ETIQUETAS break;
Tratamiento XML – XmlPull
Ahora comenzaremos a analizar si la primera etiqueta es “item”, si es la etiqueta
inicializamos el objeto noticia para poder comenzar a llenarlo con el contenido del item.
if (etiqueta.equals("item")){ noticiaActual = new Noticia(); }
Caso contrario es cuando ya no item y son las demás etiquetas que pueden ser su
contenido, propiamente dicho:
else if (noticiaActual != null){ //analizar las demas etiquetas //title, description y demás }
Tratamiento XML – XmlPull
Ahora comenzaremos a analizar si la primera etiqueta es “item”, si es la etiqueta
inicializamos el objeto noticia para poder comenzar a llenarlo con el contenido del item.
if (etiqueta.equals("link")){ noticiaActual.setLink(parser.nextText()); }else if (etiqueta.equals("description")){ noticiaActual.setDescripcion(parser.nextText()); }else if (etiqueta.equals("pubDate")){ noticiaActual.setFecha(parser.nextText()); }else if (etiqueta.equals("title")){ noticiaActual.setTitulo(parser.nextText()); }else if (etiqueta.equals("guid")){ noticiaActual.setGuid(parser.nextText()); }
Comenzaremos a leer las etiquetas y comparamos para setear el valor en el objeto Noticia.
Tratamiento XML – XmlPull
Finalmente validaremos si es el final del tag:
case XmlPullParser.END_TAG: etiqueta = parser.getName(); if (etiqueta.equals("item") && noticiaActual != null){ noticias.add(noticiaActual); } break;
El último case de nuestro switch nos indicará que ya hemos terminado el tag y agregamos el objeto tipo Noticias a nuestro listado llamado “noticias”. (*) No nos olvidemos de agregarlo en nuestro Activity.