Cum sa folosesti kXML pentru a parsa fisiere XML in aplicatii J2ME

1 comment - This post in english

kXML este un parser simplu pentru documente XML, conceput special pentru medii de dezvoltare cu restrictii asupra memoriei disponibile, cum ar fi Applet-urile sau dispozitivele mobile ce suporta aplicatii Java sau MIDP (MIDP este un standard ce indica profilul software al dispozitivului).

Problema aplicatiilor de tip J2ME MIDlets este data de faptul ca exista dispozitive mobile care nu suporta API-ul standard pentru prelucrarea documentelor XML definit de pachetul aditional JSR 280. O solutie la aceasta problema este utilizarea unui parser independent si cu cerinte minimale care sa fie rapid dar si cu un impact mic asupra memoriei disponibile (in special cea virtuala). Doua solutii posibile sunt kXML si NanoXML.

Pentru a vedea un exemplu complet de aplicatiei J2ME MIDlet care foloseste kXML pentru a parsa un XML RSS feed, cititi articolul Cum sa parsezi un XML RSS feed cu kXML intr-un J2ME MIDlet.

Metode oferite de API-ul kXML

Cu toate ca este o solutie oferita cu licenta BSD, o problema a parser-ului kXML o reprezinta documentatia care este destul de simpla si nu ofera descrieri detaliate ale metodelor. De asemenea,  exista diferente majore intre versiunea 2 (folosita in acest articol) si versiunea 1.

Pentru a intelege modul de utilizare al parser-ului, vom considera urmatorul document XML, ce reprezinta o parte simplificata dintr-un feed RSS:

<?xml version="1.0" encoding="UTF-8"?>
<channel version="1">
  <source>www.itcsolutions.eu</source>
  <news>
    <item>
      <title>Tutorial Java SCJP &#8211; #16 Constructors</title>
      <link>http://www.itcsolutions.eu/2011/04/23/tutorial-java-scjp-16-constructors/</link>
      <author>catalin.boja</author>
    </item>
    <item>
      <title>Tutorial Java SCJP &#8211; #15 Wrapper classes for primitive types</title>
      <link>http://www.itcsolutions.eu/2011/04/22/tutorial-java-scjp-15-wrapper-classes-for-primitive-types/</link>
      <author>catalin.boja</author>
    </item>
  </news>
</channel>

Daca comparati metodele si modul de utilizare a acestui parser cu API-ul unui parser din JDK-ul standard, nu uitati ca aceasta solutie a fost dezvoltata astfel incat sa minimizeze impactul asupra performantei si memoriei (care sunt oricum limitate) dispozitivului mobil. Acesta este un motiv pentru care acest parser nu foloseste memorie suplimentara pentru a construi arborele documentului XML. In schimb, ne permite sa iteram prin nodurile documentului XML.

Pentru a folosi parser-ul cu succes, trebuie sa avem o imagine clara si exacta cu privire la structura XML-ului. Acest parser nu poate fi utilizat pentru a parcurge un document XML cu o structura necunoscuta. Informatia dorita se extrage dupa ce ajungem la nodul respectiv, pornind din radacina si parcurgand nodurile anterioare lui.

Cele mai utilizate metode din clasa org.kxml2.io.KXmlParser sunt:

  • nextTag() – pozitioneaza parser-ul pe urmatorul tag de inceput sau sfarsit  (de ex. <title> sau </title>); daca urmatorul element nu este un tag (eticheta), de exemplu este textul unui nod, la apelul acestei metode se va genera o exceptie de tipul XmlPullParserException – unexpected type …
  • getName() – intoarce numele tag-ului (eticheta sau nod) curent;
  • nextText() – intoarce textul nodului curent; pentru a nu genera o exceptie, pozitia curenta a parser-ului trebuie sa fie pe eticheta de start (de ex. <title>), iar intre eticheta de start si cea de final, nodul trebuie sa contina text; dupa apel, pozitia curenta devine eticheta de final (ex. </title>);
  • skipSubTree() – parser-ul ignora orice informatie a nodului curent (inclusiv sub-nodurile acestuia), iar nodul curent devine urmatorul nod de pe acelasi nivel;
  • require() – metoda folosita pentru a verifica starea (pozitia curenta) parser-ului; este o metoda auxiliara utilizata pentru a verifica daca structura documentului XML este cea asteptata; daca nodul curent nu are tipul verificat atunci se genereaza exceptie;
  • getEventType() – intoarce un cod ce indica tipul tag-ului curent (de ex. XmlPullParser.END_TAG, XmlPullParser.START_TAG, XmlPullParser.END_DOCUMENT, …);
  • getAttributesCount() – intoarce numarul de atribute pentru nodul curent;
  • getAttributeValue() – intoarce valoarea unui atribut (este indicat printr-un index numeric ce porneste de la 0) pentru nodul curent;

Pentru documentul XML anterior, daca dorim sa obtinem al 2-lea titlu, atunci metodele descrise pot fi folosite astfel:  (este un proces iterativ care trece prin fiecare nod pana la cel dorit):

        myParser.nextTag();
        myParser.require(XmlPullParser.START_TAG, null, "channel");
        //obtin numarule de atribute
        int attCount = myParser.getAttributeCount();
        //obtin valoarea primului atribut
        String version = myParser.getAttributeValue(0);
 
        myParser.nextTag();
        //verific daca tag-ul curent este source
        myParser.require(XmlPullParser.START_TAG, null, "source");
 
        //extrag textul nodului source
        myParser.nextText();
        //verific daca tagul curent este /source - eticheta de inchidere
        myParser.require(XmlPullParser.END_TAG, null, "source");
 
        myParser.nextTag();
        //verific daca eticheta curenta este news
        myParser.require(XmlPullParser.START_TAG, null, "news");
 
        myParser.nextTag();
        //verific daca eticheta curenta este item
        myParser.require(XmlPullParser.START_TAG, null, "item");
        //ignor primul nod item
        myParser.skipSubTree();
 
        myParser.nextTag();
        //verific daca eticheta curenta este item - al 2-lea nod
        myParser.require(XmlPullParser.START_TAG, null, "item");
        myParser.nextTag();
        //verific daca eticheta curenta este title
        myParser.require(XmlPullParser.START_TAG, null, "title");
        //obtin textul nodului title
        String secondTitle = myParser.nextText();

Pentru a simplifica secventa anterioara se poate renunta la apelurile metodei require(), insa acestea sunt utile pentru a valida structura documentului XML (pe baza unei structuri asteptate).

Alte informatii despre modul de utilizare a parserului kXML 2 pot fi gasite pe site-ul proiectului http://kxml.sourceforge.net/kxml2/ sau in IBM Developer Works whitepaper.

Solutie pentru exceptia kXML  – XmlPullParserException – PI must not start with xml error

In unele situatii, dupa ce a fost initializat parser-ul kXML si apelata metoda nextTag() aplicatia genereaza o exceptie de tipul XmlPullParserException cu mesajul PI must not start with xml. In general, cauza acestei exceptii, descrisa si de Chris Winters, este ca documentul XML nu incepe cu secventa <?xml version … si are la inceput spatii sau alte caractere spciale (de ex. byte order mark (BOM)). Pentru a verifica documentul folositi un editor ca HxD, si NU Notepad sau browser-ul.

Solutia pentru a evita aceasta exceptie, este fie sa elimini acele caractere de start (daca ai acces la sursa XML-ului) sau sa copiezi continutul documentului XML intr-un alt string si sa-l formatezi.

Cum sa adaugi libraria kXML in proiectul Java ME

1. Primul lucru pe care trebuie sa-l faci este sa descarci arhiva Java kxml2-2.3.0.jar de pe kXML Website.

2. In NetBeans, selecteaza proiectul Java ME si deschide fereastra de proprietati (click-dreapta pe numele proiectului in fereastra Projects si selecteaza Properties; un alt mod este sa selectezi numele proiectului si din meniul NetBeans alegi optiunea File –> Project Properties);

3. In fereastra Project Properties selecteaza tab-ul Build –> Libraries and Resources;

4. Adauga arhiva descarcata cu butonul Add Jar/Zip;

Fereastra Project Properties in NetBeans

Fereastra Project Properties in NetBeans

5. Asigura-te ca pachetul are checkbox-ul Package bifat;

6. Pentru a folosi clasa KXmlParser, in fisierul sursa Java se adauga import org.kxml2.io.*;

Pentru a vedea un exemplu complet de aplicatiei J2ME MIDlet care foloseste kXML pentru a parsa un XML RSS feed, cititi articolul Cum sa parsezi un XML RSS feed cu kXML intr-un J2ME MIDlet.

, ,


  1. No comments yet.
(will not be published)