XML Parser (DOM & SAX)

| Drucken |

Heute möchte ich Euch zeigen, wie man einen XML Parser für einen RSS Feed schreibt.
Den kompletten Source Code findet Ihr ganz am Ende der Seite.

Wir fangen mit einem einfachen RSS Feed an. In einem fortgeschrittenen Tutorial werde ich den Parser für Youtube Playlists erweitern und in eine ListView integrieren.

 

DOM Parser vs SAX Parser

Bei der Entwicklung einer eigenen App, die mit verschiedenen XML-Dateien arbeitet, bekam ich eine OutOfMemory Exception. Nachdem mit Eclipse den heap dump analysiert hatte, fiel mir auf, dass meine Klassen, die XML mit DOM parsen, den meisten Speicher beanspruchen.

Dadurch fand ich auch den Grund dafür. Die XML-Datei, also die komplette Baumstruktur, wird mit dem DOM Parser komplett gespeichert. Ab einer bestimmten Dateigröße (etwas über 80 kb glaube ich), beansprucht dieser so viel Platz, dass der heap überläuft.

Die Lösung hierzu war der SAX Parser. Dieser speichert die XML-Einträge nicht, sondern geht diese Zeile für Zeile durch. Allerdings ist der SAX Parser merklich langsamer als der DOM Parser.

 

XML Datei

Die XML Datei vom heise.de RSS Feed für einen Eintrag sieht so aus:

<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>heise online Top-News</title>
	<subtitle>Nachrichten nicht nur aus der Welt der Computer</subtitle>
	<link href="http://www.heise.de/newsticker/" />
	<link rel="self" href="http://www.heise.de/newsticker/heise-top-atom.xml" />
	<updated>2011-01-21T15:28:00+01:00</updated>
	<author>
		<name>heise online</name>
	</author>
	<id>http://www.heise.de/newsticker/</id>

<entry>
		<title>Duke Nukem verlässt angeblich im Mai die ewige Warteschleife</title>
		<link href="http://www.heise.de/newsticker/meldung/Duke-Nukem-verlaesst-angeblich-im-Mai-die-ewige-Warteschleife-1174484.html/from/atom10" />
		<id>http://www.heise.de/newsticker/meldung/Duke-Nukem-verlaesst-angeblich-im-Mai-die-ewige-Warteschleife-1174484.html/from/atom10</id>
		<published>2011-01-21T15:28:00+01:00</published>
		<updated>2011-01-21T16:48:38+01:00</updated>
</entry>
<entry> ... </entry>
</feed>

Uns interessieren nur die <entry> Tags. In unserem Beispiel lesen wir nur den Titel ein. Die anderen Tags funktionieren ähnlich wie der Titel.

DOM Parser

  • DOM Document erzeugen

String urlString = "http://www.heise.de/newsticker/heise-top-atom.xml";
InputStream is = getInputStreamFromURL(urlString);
Wir definieren die URL zum RSS Feed (hier von heise.de).
Anschließend benutzen wir unsere Methode, die wir bereits kennen gelernt haben, um die XML Datei herunterzuladen.

 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 DocumentBuilder db;
 try {
 db = dbf.newDocumentBuilder();
 
 //parse rss feed
 Document doc = db.parse(is);
 doc.normalize();
 
 if (is != null)
readXML(doc);
Wir erzeugen uns ein DOM Document und parsen den InputStream mithilfe des DocumentBuilders. Falls der InputStream ungleich null ist, rufen wir die Methode readXML auf, mit der wir das DOM Document auslesen.

 

Der komplette Source-Code, der das Document erzeugt:

public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.main);
 String urlString = "http://www.heise.de/newsticker/heise-top-atom.xml";
 InputStream is = getInputStreamFromURL(urlString);
 
 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 DocumentBuilder db;
 try {
 db = dbf.newDocumentBuilder();
 
 //parse rss feed
 Document doc = db.parse(is);
 doc.normalize();
 
 if (is != null)
 readXML(doc);
 
 } catch (ParserConfigurationException e) {
 e.printStackTrace();
 } catch (IOException e) {
 e.printStackTrace();
 } catch (NullPointerException e){
 e.printStackTrace();
 } catch (SAXException e) {
 e.printStackTrace();
 }

 

  • XML auslesen

private void readXML(Document doc) throws NullPointerException
heißt unsere Methode, die das Document übergeben bekommt.

//get all elements named "entry"
 NodeList nl = doc.getElementsByTagName("entry");
 
 //check if NodeList has child elements
 if (nl != null && nl.getLength() > 0) {
 
 //if true, loop through all elements
 for (int i = 0 ; i < nl.getLength(); i++) {
Aus dem Document holen wir uns alle Elemente, die den Tag <entry> enthalten. Diese NodeList überprüfen wir auf Kindelemente. Falls Kindelemente vorhanden, gehen wir diese mit einer for-Schleife durch.

//get each entry
 Element entry = (Element) nl.item(i);
 
 //read the title of each entry
 Element title = (Element) entry.getElementsByTagName("title").item(0);
 
 String title = title.getFirstChild().getNodeValue();
In der for-Schleife holen wir uns den i-ten Knoten aus der NodeList und holen uns alle Elemente, die den Tag <title> enthalten. Diesen können wir dann in einem String speichern.

 

Der komplette Code der readXML Methode:

private void readXML(Document doc) throws NullPointerException {
 //get all elements named "entry"
 NodeList nl = doc.getElementsByTagName("entry");
 
 //check if NodeList has child elements
 if (nl != null && nl.getLength() > 0) {
 
 //if true, loop through all elements
 for (int i = 0 ; i < nl.getLength(); i++) {
 //get each entry
 Element entry = (Element) nl.item(i);
 
 //read the title of each entry
 Element title = (Element) entry.getElementsByTagName("title").item(0);
 
 String title = title.getFirstChild().getNodeValue();
 }
 }
 }

Zur besseren Darstellung der RSS Ergebnisse habe im Source Code noch einen ScrollView eingefügt, der ein LinearLayout enthält. Dieses LinearLayout fülle ich dynamisch in der for-Schleife mit TextViews und Platzhaltern zur Trennung der verschiedenen Einträge.
Die angepasste readXML-Methode:

private void readXML(Document doc) throws NullPointerException {
 LinearLayout layout = (LinearLayout) findViewById(R.id.layout);
 
 //get all elements named "entry"
 NodeList nl = doc.getElementsByTagName("entry");
 
 //check if NodeList has child elements
 if (nl != null && nl.getLength() > 0) {
 
 //if true, loop through all elements
 for (int i = 0 ; i < nl.getLength(); i++) {
 //get each entry
 Element entry = (Element) nl.item(i);
 
 //read the title of each entry
 Element title = (Element) entry.getElementsByTagName("title").item(0);
 
 //create TextView and set the title as text
 TextView tv_title = new TextView(this);
 tv_title.setText(title.getFirstChild().getNodeValue());
 
 //create a spacer, which shows a horizontal line between each news
 LinearLayout layout_spacer = new LinearLayout(this);
 layout_spacer.setBackgroundColor(Color.DKGRAY);
 layout_spacer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, 3));
 
 //add both new TextView and LinearLayout
 layout.addView(tv_title);
 layout.addView(layout_spacer);
 }
 }
 }

 

So sieht die fertige App aus:

Source Code

Zum Schluss wie versprochen noch der komplette Source Code: hier downloaden

 

Kommentare
Suche
Nur registrierte Benutzer können Kommentare schreiben!

3.26 Copyright (C) 2008 Compojoom.com / Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved."

The secret of any online business is web hosting. Select the best hosting service reading offered by justhost.
Free Joomla Templates designed by Joomla Hosting