Comment analyser XML à l'aide de l'analyseur SAX


Je suis ce tutoriel.

Cela fonctionne très bien mais je voudrais qu'il renvoie un tableau avec toutes les chaînes au lieu d'une seule chaîne avec le dernier élément.

Des idées comment faire cela?

Author: Deepak Swami, 2011-01-28

2 answers

Vous voulez donc construire un analyseur XML pour analyser un flux RSS comme celui-ci.

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

Vous avez maintenant deux implémentations SAX avec lesquelles vous pouvez travailler. Soit vous utilisez l'implémentation org.xml.sax ou android.sax. Je vais expliquer les pro et les con des deux après avoir posté un exemple de hander court.

Android.mise en œuvre de sax

Commençons par l'implémentation de android.sax.

Vous devez d'abord définir la structure XML en utilisant les RootElement et Element objet.

Dans tous les cas, je travaillerais avec des POJOs (Vieux objets Java simples) qui contiendraient vos données. Voici les POJOs nécessaires.

Canal.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
}

Cette classe implémente l'interface Serializable afin que vous puissiez la mettre dans un Bundle et faire quelque chose avec elle.

Maintenant, nous avons besoin d'une classe pour tenir nos objets. Dans ce cas, je vais simplement étendre la classe ArrayList.

Articles.java

public class Items extends ArrayList<Item> {

    public Items() {
        super();
    }

}

C'est tout pour notre conteneur d'articles. Nous avons maintenant besoin d'une classe pour contenir les données de chaque élément.

Article.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.

}

Exemple:

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;
    }

}

Maintenant, c'était un exemple très rapide comme vous pouvez le voir. L'avantage majeur de l'utilisation de l'implémentation android.sax SAX est que vous pouvez définir la structure du XML que vous devez analyser, puis simplement ajouter un écouteur d'événement aux éléments appropriés. L'inconvénient est que le code se répète assez et gonflé.

Org.XML.mise en œuvre de sax

Le org.xml.sax SAX l'implémentation du gestionnaire est un peu différente.

Ici, vous ne spécifiez pas ou ne déclarez pas votre structure XML mais écoutez simplement les événements. Les plus utilisés sont les événements suivants:

  • Début du document
  • Fin du document
  • Début de l'élément
  • Fin de l'élément
  • Caractères entre le début et la fin de l'élément

Un exemple d'implémentation de gestionnaire utilisant l'objet Channel ci-dessus ressemble à ce.

Exemple

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.
    }

}

Maintenant, pour être honnête, je ne peux pas vraiment vous dire de réel avantage de cette implémentation de gestionnaire par rapport à celle de android.sax. Je peux cependant vous dire l'inconvénient qui devrait être assez évident maintenant. Jetez un oeil à l'instruction else if dans la méthode startElement. En raison du fait que nous avons les balises <title>, link et description nous devons suivre là dans la structure XML que nous sommes en ce moment. C'est-à-dire que si nous rencontrons une balise de départ <item>, nous définissons l'indicateur inItem sur true pour nous assurer que nous mappons les données correctes à l'objet correct et dans la méthode endElement, nous définissons cet indicateur à false si nous rencontrons une balise </item>. Pour signaler que nous avons terminé avec cette balise d'élément.

Dans cet exemple, il est assez facile de gérer cela, mais avoir à analyser une structure plus complexe avec des balises répétitives à différents niveaux devient délicat. Là, vous devrez soit utiliser des énumérations, par exemple, pour définir votre état actuel et beaucoup d'états switch/case pour vérifier où vous êtes ou une solution plus élégante serait une sorte de traqueur de balises utilisant une pile de balises.

 179
Author: Octavian Damiean, 2013-05-18 17:46:10

Dans de nombreux problèmes, il est nécessaire d'utiliser différents types de fichiers xml à des fins différentes. Je ne vais pas essayer de saisir l'immensité et de dire de ma propre expérience ce dont j'avais besoin tout cela.

Java, peut-être, mon langage de programmation préféré. En outre, cet amour est renforcé par le fait que vous pouvez résoudre n'importe quel problème et venir avec un vélo n'est pas nécessaire.

Donc, il m'a fallu créer un tas de client-serveur exécutant une base de données qui permettrait au client pour effectuer à distance des entrées dans le serveur de base de données. Inutile de vérifier les données d'entrée, etc. et autres, mais ce n'est pas ça.

Comme principe de travail, j'ai, sans hésitation, choisi la transmission d'informations sous forme de fichier xml. Des types suivants:

<? 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> 

Rendre plus facile à lire plus loin, sauf à dire que ce sont les informations sur les institutions de médecins. Nom, prénom, identifiant unique, etc. En général, les séries de données. Ce fichier a obtenu en toute sécurité côté serveur, puis commencez à analyser le fichier.

Des deux options d'analyse (SAX vs DOM) J'ai choisi SAX vue du fait qu'il travaille plus brillant, et il a été le premier je suis tombé entre les mains:)

Donc. Comme vous le savez, pour fonctionner correctement avec l'analyseur, nous devons remplacer les méthodes nécessaires DefaultHandler. Pour commencer, connectez les paquets requis.

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

Maintenant, nous pouvons commencer à écrire notre analyseur

public class SAXPars extends DefaultHandler {
   ... 
} 

Commençons par la méthode startDocument (). Comme son nom l'indique, Il réagit à un début d'événement du document. Ici vous pouvez accrocher une variété d'actions telles que l'allocation de mémoire, ou de réinitialiser les valeurs, mais notre exemple est assez simple, il suffit donc de marquer le début des travaux d'un message approprié:

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

Suivant. L'analyseur passe par le document rencontre l'élément de sa structure. Démarre la méthode startElement (). Et en fait, son apparence ceci: startElement (String namespaceURI, String LocalName, String qName, Attributs att). Ici namespaceURI - l'espace de noms, LocalName - le nom local de l'élément, qName - une combinaison de nom local avec un espace de noms (séparés par un deux-points) et atts-les attributs de cet élément. Dans ce cas, tout simple. Il suffit d'utiliser qNom'om et de le jeter dans une ligne de service thisElement. Ainsi, nous marquons dans lequel l'élément au moment où nous sommes.

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

Ensuite, point de réunion nous arrivons à sa signification. Incluez ici les méthodes characters (). Il est de la forme: les caractères (char [] ch, int start, int length). Eh bien ici tout est clair. ch-un fichier contenant la chaîne elle-même auto-importance dans cet élément. début et longueur - le nombre de service indiquant le point de départ dans la ligne et la longueur.

@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, oui. J'ai presque oublié. Comme l'objet de ce qui sera de plier les données naparsennye parle au type de médecins. Cette classe est définie et a tous les setters-getters nécessaires.

Prochain élément évident se termine et il est suivie par la prochaine. Responsable de la fin de l'endElement (). Il nous signale que l'article est terminé et que vous pouvez faire n'importe quoi en ce moment. Va continuer. Nettoyer l'élément.

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

Venant ainsi le document entier, nous arrivons à la fin du fichier. Travail endDocument (). Dans ce document, nous pouvons libérer de la mémoire, faire une impression diagnostichesuyu,etc. Dans notre cas, il suffit d'écrire sur ce que l'analyse se termine.

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

Nous avons donc une classe pour analyser xml notre format. Voici le texte intégral:

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 ..."); 
} 
} 

J'espère que le sujet a aidé à présenter facilement l'essence de l'analyseur SAX.

Ne jugez pas strictement premier article :) J'espère que c'était au moins quelqu'un d'utile.

UPD: Pour exécuter cet analyseur, vous pouvez utiliser ce code:

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

 2
Author: Sergey Shustikov, 2014-10-24 13:07:04