2011-01-28 22 views
57

Estoy siguiendo este tutorial.Cómo analizar XML utilizando el analizador SAX

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 cómo hacer esto?

+0

Podría por favor puesto una representación abstracta de la estructura XML? –

+0

http://dearfriends.se/category/blog/feed/rss/ -> ver fuente – Johan

Respuesta

178

Así que quiere construir un analizador XML para analizar un canal RSS como este.

<rss version="0.92"> 
<channel> 
    <title>MyTitle</title> 
    <link>http://myurl.com</link> 
    <description>MyDescription</description> 
    <lastBuildDate>SomeDate</lastBuildDate> 
    <docs>http://someurl.com</docs> 
    <language>SomeLanguage</language> 

    <item> 
     <title>TitleOne</title> 
     <description><![CDATA[Some text.]]></description> 
     <link>http://linktoarticle.com</link> 
    </item> 

    <item> 
     <title>TitleTwo</title> 
     <description><![CDATA[Some other text.]]></description> 
     <link>http://linktoanotherarticle.com</link> 
    </item> 

</channel> 
</rss> 

Ahora tiene dos implementaciones de SAX con las que puede trabajar. O utiliza la implementación 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.

android.sax Implementación

Vamos a empezar con la implementación android.sax.

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

En cualquier caso, trabajaría con POJO (objetos simples de Java) que contendrían 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 ponerlo en un Bundle y hacer algo con él.

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<Item> { 

    public Items() { 
     super(); 
    } 

} 

Eso es todo para nuestro contenedor 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 <item> tag occurrence we create a new Item object. 
     chanItem.setStartElementListener(new StartElementListener() { 
      public void start(Attributes attributes) { 
       item = new Item(); 
      } 
     }); 

     // On every </item> 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 era un ejemplo muy rápida como se puede ver. La principal ventaja de utilizar la implementación de SAX 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.

org.xml.sax Implementación

La aplicación manejador org.xml.sax SAX es un poco diferente.

Aquí no especificas o declaras tu estructura XML, sino solo escuchas de eventos.Los más utilizados son los siguientes eventos:

  • Documento Start
  • Documento Final
  • Elemento Marcha
  • elemento extremo
  • caracteres entre el elemento de inicio y elemento de extremo

Un ejemplo la implementación del controlador 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, para ser honesto, no puedo realmente decir ninguna ventaja real de esta aplicación sobre el manejador de android.sax uno. Sin embargo, puedo decirte la desventaja que debería ser bastante obvia por el momento. Eche un vistazo a la declaración else if en el método startElement. Debido al hecho de que tenemos las etiquetas <title>, link y 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 <item>, configuramos 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 </item>. 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 etiquetas 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 etiquetas que usa una pila de etiquetas.

+0

@Adinia tenga en cuenta que no es un problema utilizar ambas implementaciones juntas. No hay problema en hacer eso, siempre y cuando sepa por qué lo hace. –

+0

@ octavian-damiean Es cierto que mi código funcionaba, pero realmente no sabía por qué escribía cada línea; Trato de limpiarlo un poco ahora, ya que entendí cómo funciona cada uno de ellos. Así que gracias por la nota que está bien usar ambos juntos. – Adinia

+0

@Adinia ya veo. De nada. Si tienes más preguntas al respecto, también puedes unirte a la genial [sala de chat de Android] (http://chat.stackoverflow.com/rooms/15/android). –

2

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 programació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 montón de cliente-servidor ejecutando una base de datos que permitiera al cliente realizar 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:

<? xml version = "1.0" encoding = "UTF-8" standalone = "no"?> 
<doc> 
<id> 3 </ id> 
<fam> Ivanov </ fam> 
<name> Ivan </ name> 
<otc> I. </ otc> 
<dateb> 10-03-2005 </ dateb> 
<datep> 10-03-2005 </ datep> 
<datev> 10-03-2005 </ datev> 
<datebegin> 09-06-2009 </ datebegin> 
<dateend> 10-03-2005 </ dateend> 
<vdolid> 1 </ vdolid> 
<specid> 1 </ specid> 
<klavid> 1 </ klavid> 
<stav> 2.0 </ stav> 
<progid> 1 </ progid> 
</ doc> 

Facilita la lectura, excepto para decir que es la información sobre las instituciones de los médicos. 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 sintáctico (SAX vs DOM) Me eligieron vista SAX del hecho de que él trabaja más brillante, y él fue el primero que cayó en manos :)

So. 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 empezar a escribir nuestro analizador

public class SAXPars extends DefaultHandler { 
   ... 
} 

Vamos a empezar con el método startDocument(). Él, como su nombre lo indica, reacciona a un evento que comienza con el documento. Aquí se puede colgar una variedad de acciones, tales como la asignación de memoria, o para restablecer los valores, pero nuestro ejemplo es bastante simple, por lo que sólo marcar el inicio de los trabajos 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, el elemento de la reunión nos da 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 objetivo de los datos de plegamiento naparsennye se refiere al tipo de médicos. Esta clase está definida y tiene todos los setters-getters necesarios.

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 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 tenemos 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 ayude 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 programa de análisis, puede utilizar este código:

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

Cuestiones relacionadas