I ran into this bug when I was trying to implement a simple xml parser derived from Android's "Parsing XML Data" training page. As I am a new Android developer--and an extremely amateur coder--the exception that is thrown when I try to implement my own modified version does not help in directing me towards the problem.
Here is the exact XmlPullParserException that is thrown:
04-06 19:10:42.772 19279-19279/com.example.reader.app E/Main-Activity﹕ expected: START_DOCUMENT {null}articles (position:START_TAG <articles>@2:11 in java.io.InputStreamReader@41ec5dd8)
Here is my test xml file:
<?xml version="1.0" encoding="utf-8"?>
<articles>
<article>
<title>Test Article Number 1</title>
<description>This is a short description of the article.</description>
<text>This is some text.</text>
</article>
<article>
<title>Test Article Number 2</title>
<description>This is a short description of the article.</description>
<text>This is some text.</text>
</article>
</articles>
Here is the onCreate method of my MainActivity.java file that opens the inputStream and instantiates my ArticleListParser class.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(
R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
// try to access the data in the xml file
Log.d(TAG, "Starting to execute your code...");
try {
InputStream minputStream = getResources().openRawResource(R.raw.test);
ArticleListParser marticleParser = new ArticleListParser();
List mlistOfArticles = marticleParser.parse(minputStream);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
And here is my ArticleListParser.java class. The instance of this class instantiated by the MainActivity does not make it past the parse() method (i.e., does not execute any other methods which would be triggered by the return readArticles(parser);
line in parse())...
package com.example.reader.app;
import android.util.Log;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* Created by jasondblack on 4/5/14.
*/
public class ArticleListParser {
// We don't use namespaces
private static final String ns = null;
// log tag
private static final String TAG = "ArticleListParser";
public List parse(InputStream in) throws XmlPullParserException, IOException {
try {
// get a new xml pull parser
XmlPullParser parser = Xml.newPullParser();
// indicate that this xml does not have namespaces
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
// set the input stream of the pull parser
parser.setInput(in, null);
// go to the first element
parser.nextTag();
// start reading the xml
return readArticles(parser);
} finally {
// no matter what, close the input stream
in.close();
}
}
private List readArticles(XmlPullParser parser) throws XmlPullParserException, IOException {
// create List that will be returned
List articles = new ArrayList();
parser.require(XmlPullParser.START_DOCUMENT, ns, "articles");
while(parser.next() != XmlPullParser.END_TAG) {
if(parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
String thisName = parser.getName();
if(thisName.equals("article")) {
articles.add(readArticle(parser));
}
else {
skip(parser);
}
}
return articles;
}
public static class Article {
public final String title;
public final String description;
public final String text;
private Article (String thisTitle, String thisDescription, String thisText) {
this.title = thisTitle;
this.description = thisDescription;
this.text = thisText;
}
}
private Article readArticle(XmlPullParser parser) throws XmlPullParserException, IOException {
// make sure the xml parser is at the beginning of a tag
parser.require(XmlPullParser.START_TAG, ns, "article");
String title = null;
String description = null;
String text = null;
// step through xml file until you reach the end tag
while(parser.getEventType() != XmlPullParser.END_TAG) {
// if it is the start tag, continue onto next element in parser
// ??? weird because we have already required a start tag in the first line of this method
if(parser.getEventType() != XmlPullParser.START_TAG) {
continue;
}
// get the name of the element in the angled brackets
String name = parser.getName();
// check to see if it is one of the titles we want ... if so extract the contents
if(name.equals("title")) {
title = readTitle(parser);
} else if (name.equals("description")) {
description = readDescription(parser);
} else if (name.equals("text")) {
text = readText(parser);
} else {
skip(parser);
}
}
// return an Article object
return new Article (title, description, text);
}
// TODO: stopped here don't be confused by duplicate methods implement your own readTitle(), readDescription(), readText(), and extractText() [their "readTExt()" method]
// Processes title tags in the feed
private String readTitle(XmlPullParser parser) throws IOException, XmlPullParserException {
parser.require(XmlPullParser.START_TAG, ns, "title");
String thisTitle = readText(parser);
parser.require(XmlPullParser.END_TAG, ns, "title");
return thisTitle;
}
// Processes description tags in the feed.
private String readDescription(XmlPullParser parser) throws IOException, XmlPullParserException {
parser.require(XmlPullParser.START_TAG, ns, "description");
String thisDescription = readText(parser);
parser.require(XmlPullParser.END_TAG, ns, "description");
return thisDescription;
}
// For the tags title and summary, extracts their text values.
private String readText(XmlPullParser parser) throws IOException, XmlPullParserException {
String result = "";
// TODO: possible that this will snag on my xml because I don't have type="text" in my xml elements
if (parser.next() == XmlPullParser.TEXT) {
result = parser.getText();
parser.nextTag();
}
return result;
}
private void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
if (parser.getEventType() != XmlPullParser.START_TAG) {
throw new IllegalStateException();
}
int depth = 1;
while (depth != 0) {
switch (parser.next()) {
case XmlPullParser.END_TAG:
depth--;
break;
case XmlPullParser.START_TAG:
depth++;
break;
}
}
}
}