Урок 79. XmlPullParser. Парсим XML


В этом уроке:

- парсим XML с помощью XmlPullParser

XmlPullParser – XML-парсер, который можно использовать для разбора XML документа. Принцип его работы заключается в том, что он пробегает весь документ, останавливаясь на его элементах. Но пробегает он не сам, а с помощью метода next. Мы постоянно вызываем метод next и с помощью метода getEventType проверяем, на каком элементе парсер остановился.

Основные элементы документа, которые ловит парсер:

START_DOCUMENT – начало документа

START_TAG – начало тэга

TEXT – содержимое элемента

END_TAG – конец тэга

END_DOCUMENT – конец документа


Напишем приложение, которое возьмет xml-файл и разберет его на тэги и аттрибуты.


Создадим проект:

Project name: P0791_ XmlPullParser
Build Target: Android 2.3.3
Application name: XmlPullParser
Package name: ru.startandroid.develop.p0791xmlpullparser
Create Activity: MainActivity


В папке res создайте папку xml, и в ней создайте файл data.xml:

<?xml version="1.0" encoding="utf-8"?>
<data>
<phone>
<company>Samsung</company>
<model>Galaxy</model>
<price>18000</price>
<screen multitouch="yes" resolution="320x480">3</screen>
<colors>
<color>black</color>
<color>white</color>
</colors>
</phone>
</data>


Это файл с описанием телефона Samsung Galaxy. Указаны его цена, характеристики экрана и возможные цвета корпуса. Данные выдуманы и могут не совпадать с реальностью :)


MainActivity.java:

package ru.startandroid.develop.p0791xmlpullparser;

import java.io.IOException;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import android.app.Activity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;

public class MainActivity extends Activity {

 
final String LOG_TAG = "myLogs";

 
/** Called when the activity is first created. */
 
@Override
 
public void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);
    setContentView
(R.layout.main);
    String tmp =
"";

   
try {
     
XmlPullParser xpp = prepareXpp();

     
while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
       
switch (xpp.getEventType()) {
       
// начало документа
       
case XmlPullParser.START_DOCUMENT:
          Log.d
(LOG_TAG, "START_DOCUMENT");
         
break;
       
// начало тэга
       
case XmlPullParser.START_TAG:
          Log.d
(LOG_TAG, "START_TAG: name = " + xpp.getName()
             
+ ", depth = " + xpp.getDepth() + ", attrCount = "
             
+ xpp.getAttributeCount());
          tmp =
"";
         
for (int i = 0; i < xpp.getAttributeCount(); i++) {
           
tmp = tmp + xpp.getAttributeName(i) + " = "
               
+ xpp.getAttributeValue(i) + ", ";
         
}
         
if (!TextUtils.isEmpty(tmp))
           
Log.d(LOG_TAG, "Attributes: " + tmp);
         
break;
       
// конец тэга
       
case XmlPullParser.END_TAG:
          Log.d
(LOG_TAG, "END_TAG: name = " + xpp.getName());
         
break;
       
// содержимое тэга
       
case XmlPullParser.TEXT:
          Log.d
(LOG_TAG, "text = " + xpp.getText());
         
break;

       
default:
         
break;
       
}
       
// следующий элемент
       
xpp.next();
     
}
     
Log.d(LOG_TAG, "END_DOCUMENT");

   
} catch (XmlPullParserException e) {
     
e.printStackTrace();
   
} catch (IOException e) {
     
e.printStackTrace();
   
}
  }

 
XmlPullParser prepareXpp() {
   
return getResources().getXml(R.xml.data);
 
}
}


В onCreate мы получаем XmlPullParser с помощью метода prepareXpp и начинаем его разбирать. Затем в цикле while мы запускаем прогон документа, пока не достигнем конца - END_DOCUMENT. Прогон обеспечивается методом next в конце цикла while. В switch мы проверяем на каком элементе остановился парсер.

START_DOCUMENT – начало документа

START_TAG – начало тега. Выводим в лог имя тэга, его уровень в дереве тэгов (глубину) и количество атрибутов. Следующей строкой выводим имена и значения атрибутов, если они есть.

END_TAG – конец тэга. Выводим только имя.

TEXT – содержимое тэга


В методе prepareXpp мы подготавливаем XmlPullParser. Для этого вытаскиваем данные из папки res/xml. Это аналогично вытаскиванию строк или картинок – сначала получаем доступ к ресурсам (getResources), затем вызываем метод, соответствующий ресурсу. В нашем случае это - метод getXml. Но возвращает он не xml-строку , а готовый XmlPullParser.

Все сохраним и запустим приложение.


Смотрим лог:

START_DOCUMENT
START_DOCUMENT
START_TAG: name = data, depth = 1, attrCount = 0
START_TAG: name = phone, depth = 2, attrCount = 0
START_TAG: name = company, depth = 3, attrCount = 0
text = Samsung
END_TAG: name = company
START_TAG: name = model, depth = 3, attrCount = 0
text = Galaxy
END_TAG: name = model
START_TAG: name = price, depth = 3, attrCount = 0
text = 18000
END_TAG: name = price
START_TAG: name = screen, depth = 3, attrCount = 2
Attributes: multitouch = yes, resolution = 320x480,
text = 3
END_TAG: name = screen
START_TAG: name = colors, depth = 3, attrCount = 0
START_TAG: name = color, depth = 4, attrCount = 0
text = black
END_TAG: name = color
START_TAG: name = color, depth = 4, attrCount = 0
text = white
END_TAG: name = color
END_TAG: name = colors
END_TAG: name = phone
END_TAG: name = data
END_DOCUMENT
START_DOCUMENT
START_DOCUMENT
START_TAG: name = data, depth = 1, attrCount = 0
START_TAG: name = phone, depth = 2, attrCount = 0
START_TAG: name = company, depth = 3, attrCount = 0
text = Samsung
END_TAG: name = company
START_TAG: name = model, depth = 3, attrCount = 0
text = Galaxy
END_TAG: name = model
START_TAG: name = price, depth = 3, attrCount = 0
text = 18000
END_TAG: name = price
START_TAG: name = screen, depth = 3, attrCount = 2
Attributes: multitouch = yes, resolution = 320x480,
text = 3
END_TAG: name = screen
START_TAG: name = colors, depth = 3, attrCount = 0
START_TAG: name = color, depth = 4, attrCount = 0
text = black
END_TAG: name = color
START_TAG: name = color, depth = 4, attrCount = 0
text = white
END_TAG: name = color
END_TAG: name = colors
END_TAG: name = phone
END_TAG: name = data
END_DOCUMENT


START_DOCUMENT срабатывает два раза по неведомым мне причинам. Далее можно наблюдать, как парсер останавливается в начале каждого тега и дает нам информацию о нем: имя, уровень (глубина), количество атрибутов, имена и названия атрибутов, текст. Также он останавливается в конце тега и мы выводим имя. В конце парсер говорит, что документ закончен END_DOCUMENT.


Если xml у вас не в файле, а получен откуда-либо, то XmlPullParser надо создавать другим способом. Перепишем метод prepareXpp:

  XmlPullParser prepareXpp() throws XmlPullParserException {
   
// получаем фабрику
   
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
   
// включаем поддержку namespace (по умолчанию выключена)
   
factory.setNamespaceAware(true);
   
// создаем парсер
   
XmlPullParser xpp = factory.newPullParser();
   
// даем парсеру на вход Reader
   
xpp.setInput(new StringReader(
       
"<data><phone><company>Samsung</company></phone></data>"));
   
return xpp;
 
}

Здесь мы сами создаем парсер с помощью фабрики, включаем поддержку namespace (в нашем случае это не нужно, на всякий случай показываю) и даем парсеру на вход поток из xml-строки (укороченный вариант data.xml).

Все сохраним и запустим. Смотрим лог:

START_DOCUMENT
START_TAG: name = data, depth = 1, attrCount = 0
START_TAG: name = phone, depth = 2, attrCount = 0
START_TAG: name = company, depth = 3, attrCount = 0
text = Samsung
END_TAG: name = company
END_TAG: name = phone
END_TAG: name = data
END_DOCUMENT


Здесь уже START_DOCUMENT сработал один раз, как и должно быть. Ну и далее идут данные элементов документа.