Урок 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 сработал один раз, как и должно быть. Ну и далее идут данные элементов документа.