web-dev-qa-db-fra.com

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

Je suis ce tutorial .

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

Des idees pour faire cela?

58
Johan

Vous voulez donc créer 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 pouvez maintenant travailler avec deux implémentations SAX. Soit vous utilisez l’implémentation org.xml.sax ou Android.sax. Je vais vous expliquer les avantages et les inconvénients de chacun d’eux après avoir publié un court exemple.

Implémentation Android.sax

Commençons par l'implémentation Android.sax.

Vous devez d’abord définir la structure XML à l’aide des objets RootElement et Element.

Dans tous les cas, je travaillerais avec des POJO (Plain Old Java Objects) qui contiendraient vos données. Voici les POJO nécessaires.

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
}

Cette classe implémente l'interface Serializable afin que vous puissiez la placer dans une Bundle et en faire quelque chose.

Nous avons maintenant 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 pour notre conteneur d'articles. Nous avons maintenant besoin d'une classe pour stocker les données de chaque élément.

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.

}

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

}

Comme vous pouvez le constater, c’était un exemple très rapide. Le principal avantage de l’implémentation Android.sax de SAX est que vous pouvez définir la structure du XML que vous devez analyser, puis d’ajouter un écouteur d’événement aux éléments appropriés. L'inconvénient est que le code est assez répétitif et gonflé.

Mise en oeuvre org.xml.sax

L’implémentation du gestionnaire org.xml.sax SAX est un peu différente.

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

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

Voici un exemple d'implémentation de gestionnaire utilisant l'objet Channel ci-dessus.

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 vous dire aucun avantage réel de cette implémentation de gestionnaire par rapport à celle de Android.sax. Je peux cependant vous dire le désavantage qui devrait être assez évident maintenant. Examinez l’instruction else if dans la méthode startElement. Etant donné que nous avons les balises <title>, link et description, nous devons effectuer le suivi 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 de mapper les données correctes sur le bon objet et dans la méthode endElement, nous mettons cet indicateur à false si nous rencontrons une balise </item>. Pour signaler que nous en avons terminé avec cette balise d'item.

Dans cet exemple, il est assez facile de gérer cela, mais le fait d’analyser une structure plus complexe avec des balises répétitives à différents niveaux devient difficile. Vous devrez alors utiliser Enums, par exemple, pour définir votre statut actuel et de nombreux statemenets switch/case pour vérifier votre position, ou une solution plus élégante consisterait en une sorte de suivi des balises utilisant une pile de balises.

179
Octavian Damiean

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

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

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

Comme principe de travail, j’ai, sans hésiter, 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, sauf pour dire que ce sont des informations sur les établissements de médecins. Nom, prénom, identifiant unique, etc. En général, la série de données. Ce fichier s’est en toute sécurité retrouvé du côté serveur, puis commence à analyser le fichier. 

Parmi les deux options d'analyse (SAX vs DOM), j'ai choisi SAX vu le fait qu'il travaille plus fort, et il a été le premier à tomber dans les mains :) 

Alors. Comme vous le savez, pour que l’analyseur fonctionne correctement, nous devons redéfinir les méthodes nécessaires, DefaultHandler's. Pour commencer, connectez les packages 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 suspendre diverses actions telles que l'allocation de mémoire ou la réinitialisation des valeurs, mais notre exemple est assez simple. Il suffit donc de marquer le début du travail 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 est la suivante: startElement (String namespaceURI, String localName, String qName, Attributes atts). Ici namespaceURI - l’espace de nommage, localName - le nom local de l’élément, qName - une combinaison de nom local avec un espace de nommage (séparé par deux points) et atts - les attributs de cet élément. Dans ce cas, tout simple. Il suffit d’utiliser qName'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, l’objet de la réunion nous permet de comprendre sa signification. Ici, incluez les méthodes caractères (). Il a la forme: caractères (char [] ch, int start, int length). Eh bien ici tout est clair. ch - un fichier contenant la chaîne elle-même d'importance dans cet élément. start et length - nombre de services indiquant le point de départ de la ligne et de 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 qui sera de plier les données naparsennye parle du type de médecins. Cette classe est définie et possède tous les setters-getters nécessaires. 

Le prochain élément évident se termine et il est suivi du suivant. Responsable de mettre fin à endElement (). Cela nous signale que l'article est terminé et que vous pouvez faire n'importe quoi pour le moment. Va procéder. Élément de nettoyage. 

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

En arrivant au document entier, nous arrivons à la fin du fichier. Travailler endDocument (). Nous pouvons y libérer de la mémoire, effectuer des diagnostics, des impressions, etc. Dans notre cas, il suffit d’écrire sur la fin de l’analyse. 

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

Nous avons donc eu 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 le premier article :) J'espère que c'était au moins une personne 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
Sergey Shustikov
public class MainActivity extends AppCompatActivity {
   ListView lvPcsPost;
    ArrayList<String> name;
    ArrayList<String> price;
    ArrayList<String> Description;
    LayoutInflater layoutInflater;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        lvPcsPost = (ListView) findViewById(R.id.lvPcsPost);
        name = new ArrayList<>();
        price = new ArrayList<>();
        Description = new ArrayList<>();
        new PostAsync().execute();
    }

    class PostAsync extends AsyncTask<Void, Void, Void> {
        ProgressDialog pd;
        XMLHelper helper;


        @Override
        protected void onPreExecute() {
            pd = ProgressDialog.show(MainActivity.this, "", "Loading...", true, false);
        }

        @Override
        protected Void doInBackground(Void... arg0) {
            helper = new XMLHelper();
            helper.get();
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            PostBaseAdapter postBaseAdapter = new PostBaseAdapter();
            lvPcsPost.setAdapter(postBaseAdapter);
            pd.dismiss();
        }

    }

    public class XMLHelper extends DefaultHandler {

        private String URL_MAIN = "http://uat.winitsoftware.com/ThemeManager/Data/Products/Products.xml";
        String TAG = "XMLHelper";

        Boolean currTag = false;
        String currTagVal = "";

        public void get() {
            try {
                SAXParserFactory factory = SAXParserFactory.newInstance();
                SAXParser mSaxParser = factory.newSAXParser();
                XMLReader mXmlReader = mSaxParser.getXMLReader();
                mXmlReader.setContentHandler(this);
                InputStream mInputStream = new URL(URL_MAIN).openStream();
                mXmlReader.parse(new InputSource(mInputStream));
            } catch (Exception e) {
                Log.e(TAG, "Exception: " + e.getMessage());
            }
        }

        @Override
        public void characters(char[] ch, int start, int length)
                throws SAXException {
            if (currTag) {
                currTagVal = currTagVal + new String(ch, start, length);
                currTag = false;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName)
                throws SAXException {
            currTag = false;

            if (localName.equalsIgnoreCase("Name"))
                name.add(currTagVal);

            else if (localName.equalsIgnoreCase("Description"))
             Description.add(currTagVal);

            else if (localName.equalsIgnoreCase("Price"))
              price.add(currTagVal);

        }
        @Override
        public void startElement(String uri, String localName, String qName,
                                 Attributes attributes) throws SAXException {
            Log.i(TAG, "TAG: " + localName);

            currTag = true;
            currTagVal = "";
            if (localName.equals("Products"));
        }
    }

    public class PostBaseAdapter extends BaseAdapter {

        public PostBaseAdapter() {

        }

        @Override
        public int getCount() {
            return name.size();
        }

        @Override
        public Object getItem(int position) {
            return name.get(position);
        }

        @Override
        public long getItemId(int position) {
            return 0;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            layoutInflater = LayoutInflater.from(getApplicationContext());

            convertView = layoutInflater.inflate(R.layout.list_item_post, parent, false);
            TextView  txtPrice = (TextView) convertView.findViewById(R.id.txtPrice);
            TextView  txtDescription = (TextView) convertView.findViewById(R.id.txtDescription);
            TextView   txtName = (TextView) convertView.findViewById(R.id.txtName);
            ImageView   image = (ImageView) convertView.findViewById(R.id.Image);
            ImageView  bigImage = (ImageView) convertView.findViewById(R.id.BigImage);

                txtPrice.setText("Price : "+price.get(position));
                 txtDescription.setText("Description : "+Description.get(position));
                txtName.setText("Name : "+name.get(position));

            return convertView;
        }
    }
}
0
vimal singh