Cómo analizar XML utilizando el analizador SAX

Estoy siguiendo este tutorial .

Funciona muy bien, pero me gustaría que devuelva una matriz con todas las cadenas en lugar de una sola cadena con el último elemento.

¿Alguna idea de como hacer esto?

Entonces, quiere construir un analizador XML para analizar un feed RSS como este.

  MyTitle http://myurl.com MyDescription SomeDate http://someurl.com SomeLanguage  TitleOne  http://linktoarticle.com   TitleTwo  http://linktoanotherarticle.com    

Ahora tiene dos implementaciones de SAX con las que puede trabajar. O utiliza la org.xml.sax o android.sax . Voy a explicar los pros y los contras de ambos después de publicar un ejemplo de mano corta.

Implementación de android.sax

Comencemos con la implementación de android.sax .

Primero tiene que definir la estructura XML utilizando los objetos RootElement y Element .

En cualquier caso, trabajaría con POJO (Plain Old Java Objects) que contendría sus datos. Aquí estarían los POJO necesarios.

Channel.java

 public class Channel implements Serializable { private Items items; private String title; private String link; private String description; private String lastBuildDate; private String docs; private String language; public Channel() { setItems(null); setTitle(null); // set every field to null in the constructor } public void setItems(Items items) { this.items = items; } public Items getItems() { return items; } public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } // rest of the class looks similar so just setters and getters } 

Esta clase implementa la interfaz Serializable para que pueda colocarla en un Bundle y hacer algo con ella.

Ahora necesitamos una clase para guardar nuestros artículos. En este caso, voy a extender la clase ArrayList .

Items.java

 public class Items extends ArrayList { public Items() { super(); } } 

Eso es para nuestro contenedor de artículos. Ahora necesitamos una clase para contener los datos de cada elemento individual.

Item.java

 public class Item implements Serializable { private String title; private String description; private String link; public Item() { setTitle(null); setDescription(null); setLink(null); } public void setTitle(String title) { this.title = title; } public String getTitle() { return title; } // same as above. } 

Ejemplo:

 public class Example extends DefaultHandler { private Channel channel; private Items items; private Item item; public Example() { items = new Items(); } public Channel parse(InputStream is) { RootElement root = new RootElement("rss"); Element chanElement = root.getChild("channel"); Element chanTitle = chanElement.getChild("title"); Element chanLink = chanElement.getChild("link"); Element chanDescription = chanElement.getChild("description"); Element chanLastBuildDate = chanElement.getChild("lastBuildDate"); Element chanDocs = chanElement.getChild("docs"); Element chanLanguage = chanElement.getChild("language"); Element chanItem = chanElement.getChild("item"); Element itemTitle = chanItem.getChild("title"); Element itemDescription = chanItem.getChild("description"); Element itemLink = chanItem.getChild("link"); chanElement.setStartElementListener(new StartElementListener() { public void start(Attributes attributes) { channel = new Channel(); } }); // Listen for the end of a text element and set the text as our // channel's title. chanTitle.setEndTextElementListener(new EndTextElementListener() { public void end(String body) { channel.setTitle(body); } }); // Same thing happens for the other elements of channel ex. // On every  tag occurrence we create a new Item object. chanItem.setStartElementListener(new StartElementListener() { public void start(Attributes attributes) { item = new Item(); } }); // On every  tag occurrence we add the current Item object // to the Items container. chanItem.setEndElementListener(new EndElementListener() { public void end() { items.add(item); } }); itemTitle.setEndTextElementListener(new EndTextElementListener() { public void end(String body) { item.setTitle(body); } }); // and so on // here we actually parse the InputStream and return the resulting // Channel object. try { Xml.parse(is, Xml.Encoding.UTF_8, root.getContentHandler()); return channel; } catch (SAXException e) { // handle the exception } catch (IOException e) { // handle the exception } return null; } } 

Ahora que fue un ejemplo muy rápido como puedes ver. La principal ventaja de utilizar la implementación de SAX de android.sax es que puede definir la estructura del XML que tiene que analizar y luego simplemente agregar un detector de eventos a los elementos apropiados. La desventaja es que el código se vuelve bastante repetitivo e hinchado.

Implementación de org.xml.sax

La implementación del controlador org.xml.sax SAX es un poco diferente.

Aquí no especifica ni declara su estructura XML, solo escucha eventos. Los más utilizados son los siguientes eventos:

  • Inicio de documento
  • Fin del documento
  • Elemento de inicio
  • Elemento final
  • Caracteres entre el inicio del elemento y el final del elemento

Una implementación de controlador de ejemplo que utiliza el objeto Canal anterior se parece a esto.

Ejemplo

 public class ExampleHandler extends DefaultHandler { private Channel channel; private Items items; private Item item; private boolean inItem = false; private StringBuilder content; public ExampleHandler() { items = new Items(); content = new StringBuilder(); } public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException { content = new StringBuilder(); if(localName.equalsIgnoreCase("channel")) { channel = new Channel(); } else if(localName.equalsIgnoreCase("item")) { inItem = true; item = new Item(); } } public void endElement(String uri, String localName, String qName) throws SAXException { if(localName.equalsIgnoreCase("title")) { if(inItem) { item.setTitle(content.toString()); } else { channel.setTitle(content.toString()); } } else if(localName.equalsIgnoreCase("link")) { if(inItem) { item.setLink(content.toString()); } else { channel.setLink(content.toString()); } } else if(localName.equalsIgnoreCase("description")) { if(inItem) { item.setDescription(content.toString()); } else { channel.setDescription(content.toString()); } } else if(localName.equalsIgnoreCase("lastBuildDate")) { channel.setLastBuildDate(content.toString()); } else if(localName.equalsIgnoreCase("docs")) { channel.setDocs(content.toString()); } else if(localName.equalsIgnoreCase("language")) { channel.setLanguage(content.toString()); } else if(localName.equalsIgnoreCase("item")) { inItem = false; items.add(item); } else if(localName.equalsIgnoreCase("channel")) { channel.setItems(items); } } public void characters(char[] ch, int start, int length) throws SAXException { content.append(ch, start, length); } public void endDocument() throws SAXException { // you can do something here for example send // the Channel object somewhere or whatever. } } 

Ahora bien, para ser sincero, realmente no puedo contarle ninguna ventaja real de la implementación de este manejador sobre el android.sax . Sin embargo, puedo decirte la desventaja que debería ser bastante obvia por el momento. Eche un vistazo a la statement else if en el método startElement . Debido al hecho de que tenemos las tags , el link y la description , tenemos que rastrear allí en la estructura XML en la que nos encontramos en este momento. Es decir, si encontramos una etiqueta de inicio , establecemos el indicador inItem en true para asegurarnos de que correlacionamos los datos correctos con el objeto correcto y en el método endElement establecemos ese indicador en false si encontramos una etiqueta . Para indicar que hemos terminado con esa etiqueta de artículo.

En este ejemplo, es bastante fácil de administrar, pero tener que analizar una estructura más compleja con tags repetitivas en diferentes niveles se vuelve complicado. Allí tendrías que usar Enums por ejemplo para establecer tu estado actual y una gran cantidad de switch / case statemenets para verificar dónde estás o una solución más elegante sería algún tipo de rastreador de tags que usa una stack de tags.

En muchos problemas, es necesario utilizar diferentes tipos de archivos xml para diferentes propósitos. No intentaré comprender la inmensidad y contaré por mi propia experiencia lo que necesitaba todo esto.

Java, tal vez, mi lenguaje de progtwigción favorito. Además, este amor se ve reforzado por el hecho de que puedes resolver cualquier problema y pensar que una bicicleta no es necesaria.

Entonces, me llevó a crear un grupo de cliente-servidor que ejecutaba una base de datos que le permitía al cliente hacer entradas de forma remota en el servidor de la base de datos. No es necesario comprobar los datos de entrada, etc. y similares, pero no se trata de eso.

Como principio de trabajo, yo, sin dudarlo, elegí la transmisión de información en forma de archivo xml. De los siguientes tipos:

    3   Ivanov   Ivan   I.   10-03-2005   10-03-2005   10-03-2005   09-06-2009   10-03-2005   1   1   1   2.0   1   

Haga que sea más fácil leer más, excepto para decir que es la información sobre instituciones médicas. Apellido, nombre, identificación única, etc. En general, la serie de datos. Este archivo se transfirió de manera segura por el lado del servidor y luego comenzó a analizar el archivo.

De las dos opciones de análisis (SAX vs DOM) elegí la vista SAX del hecho de que él trabaja más shiny, y él fue el primero en caer en las manos 🙂

Asi que. Como sabe, para trabajar con éxito con el analizador, debemos anular los métodos necesarios DefaultHandler’s. Para comenzar, conecta los paquetes requeridos.

 import org.xml.sax.helpers.DefaultHandler; import org.xml.sax. *; 

Ahora podemos comenzar a escribir nuestro analizador

 public class SAXPars extends DefaultHandler {  ... } 

Comencemos con el método startDocument (). Él, como su nombre lo indica, reactjs a un evento que comienza con el documento. Aquí puede colgar una variedad de acciones, como la asignación de memoria, o restablecer los valores, pero nuestro ejemplo es bastante simple, así que simplemente marque el comienzo del trabajo de un mensaje apropiado:

 Override public void startDocument () throws SAXException {  System.out.println ("Start parse XML ..."); } 

Siguiente. El analizador pasa por el documento cumple con el elemento de su estructura. Inicia el método startElement (). Y, de hecho, su apariencia esta: startElement (String namespaceURI, String localName, String qName, Atts de atributos). Aquí namespaceURI – el espacio de nombres, localName – el nombre local del elemento, qName – una combinación de nombre local con un espacio de nombres (separados por dos puntos) y atts – los atributos de este elemento. En este caso, todo simple. Basta usar qName’om y lanzarlo a alguna línea de servicio thisElement. Así marcamos en qué elemento estamos en este momento.

 @Override public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {  thisElement = qName; } 

A continuación, cumpliendo el elemento, llegamos a su significado. Aquí incluye los métodos characters (). Él tiene la forma: caracteres (char [] ch, int start, int longitud). Bueno, aquí todo está claro. ch – un archivo que contiene la propia cadena de auto-importancia dentro de este elemento. inicio y duración: el número de servicio que indica el punto de partida en la línea y la longitud.

 @Override public void characters (char [] ch, int start, int length) throws SAXException {  if (thisElement.equals ("id")) {     doc.setId (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("fam")) {     doc.setFam (new String (ch, start, length));  }  if (thisElement.equals ("name")) {     doc.setName (new String (ch, start, length));  }  if (thisElement.equals ("otc")) {     doc.setOtc (new String (ch, start, length));  }  if (thisElement.equals ("dateb")) {     doc.setDateb (new String (ch, start, length));  }  if (thisElement.equals ("datep")) {     doc.setDatep (new String (ch, start, length));  }  if (thisElement.equals ("datev")) {     doc.setDatev (new String (ch, start, length));  }  if (thisElement.equals ("datebegin")) {     doc.setDatebegin (new String (ch, start, length));  }  if (thisElement.equals ("dateend")) {     doc.setDateend (new String (ch, start, length));  }  if (thisElement.equals ("vdolid")) {     doc.setVdolid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("specid")) {     doc.setSpecid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("klavid")) {     doc.setKlavid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("stav")) {     doc.setStav (new Float (new String (ch, start, length)));  }  if (thisElement.equals ("progid")) {     doc.setProgid (new Integer (new String (ch, start, length)));  } } 

Ah, sí. Casi lo olvido. Como el objective de los datos de plegamiento naparsennye se refiere al tipo de médicos. Esta clase está definida y tiene todos los setters-getters necesarios.

El siguiente elemento obvio termina y es seguido por el siguiente. Responsable de finalizar endElement (). Nos indica que el artículo ha finalizado y que puede hacer cualquier cosa en este momento. Se procedera. Limpiar Elemento.

 @Override public void endElement (String namespaceURI, String localName, String qName) throws SAXException {  thisElement = ""; } 

Viniendo así todo el documento, llegamos al final del archivo. Work endDocument (). En él, podemos liberar memoria, hacer algunos diagnósticos, imprimir, etc. En nuestro caso, solo escriba sobre qué análisis finaliza.

 @Override public void endDocument () {  System.out.println ("Stop parse XML ..."); } 

Así que obtuvimos una clase para analizar xml nuestro formato. Aquí está el texto completo:

 import org.xml.sax.helpers.DefaultHandler; import org.xml.sax. *; public class SAXPars extends DefaultHandler { Doctors doc = new Doctors (); String thisElement = ""; public Doctors getResult () {  return doc; } @Override public void startDocument () throws SAXException {  System.out.println ("Start parse XML ..."); } @Override public void startElement (String namespaceURI, String localName, String qName, Attributes atts) throws SAXException {  thisElement = qName; } @Override public void endElement (String namespaceURI, String localName, String qName) throws SAXException {  thisElement = ""; } @Override public void characters (char [] ch, int start, int length) throws SAXException {  if (thisElement.equals ("id")) {     doc.setId (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("fam")) {     doc.setFam (new String (ch, start, length));  }  if (thisElement.equals ("name")) {     doc.setName (new String (ch, start, length));  }  if (thisElement.equals ("otc")) {     doc.setOtc (new String (ch, start, length));  }  if (thisElement.equals ("dateb")) {     doc.setDateb (new String (ch, start, length));  }  if (thisElement.equals ("datep")) {     doc.setDatep (new String (ch, start, length));  }  if (thisElement.equals ("datev")) {     doc.setDatev (new String (ch, start, length));  }  if (thisElement.equals ("datebegin")) {     doc.setDatebegin (new String (ch, start, length));  }  if (thisElement.equals ("dateend")) {     doc.setDateend (new String (ch, start, length));  }  if (thisElement.equals ("vdolid")) {     doc.setVdolid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("specid")) {     doc.setSpecid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("klavid")) {     doc.setKlavid (new Integer (new String (ch, start, length)));  }  if (thisElement.equals ("stav")) {     doc.setStav (new Float (new String (ch, start, length)));  }  if (thisElement.equals ("progid")) {     doc.setProgid (new Integer (new String (ch, start, length)));  } } @Override public void endDocument () {  System.out.println ("Stop parse XML ..."); } } 

Espero que el tema haya ayudado a presentar fácilmente la esencia del analizador SAX.

No juzgue estrictamente el primer artículo 🙂 Espero que al menos haya alguien útil.

UPD: Para ejecutar este analizador, puede usar este código:

 SAXParserFactory factory = SAXParserFactory.newInstance (); SAXParser parser = factory.newSAXParser (); SAXPars saxp = new SAXPars (); parser.parse (new File ("..."), saxp);