Question

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;
            }
        }
    }
}
Was it helpful?

Solution

If you add the following before your line parser.require() call in readArticles(), you can see that you're not actually at the START_DOCUMENT event at that point.

int eventType = parser.getEventType();
Log.i("TAG", "The event type is: " + eventType);

while (eventType != XmlPullParser.START_DOCUMENT) {
    eventType = parser.next();            
    Log.i("TAG", "The event type is: " + eventType);
}

After commenting out the parser.nextTag(); line in parse(), this logging shows that you're now at START_DOCUMENT. But then the require() call fails with the exception mentioned in your question. It appears that using ns, which is null, for the namespace argument isn't valid. Setting both the namespace and name arguments to null does allow the require() to succeed, but may not be what you want.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top