Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Android

Boyan Angelov
Boyan Angelov
26,485 Points

Custom BlogReader app: Getting XML/JSON data

Hi everyone! I am trying to customise the blog reader app so that it reads from my personal website.

The problem is that all I can get as input for the app at the moment is an XML feed. I wondered if anyone knows an easy way to covert this to JSON? Or if there is an easy way to use XML itself in the app?

Thanks!

6 Answers

John Coffin
John Coffin
10,359 Points

Here's a quick go at it. As this is for demo purposes only, I'm using the StackOverflowXmlParser "as is" (see the downloadable project from the link above). This doesn't include "author" information, but anyone who would want to use this for their own blog would need to customize it anyways.

The meat can be found in extractBlogPostsStackOverflowXml:

    protected void extractBlogPostsStackOverflowXml(ArrayList<HashMap<String, String>> blogPosts, HttpURLConnection connection) throws IOException, XmlPullParserException {
        StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
        InputStream inputStream = connection.getInputStream();
        List<Entry> entries = stackOverflowXmlParser.parse(inputStream);
        for (int i = 0; i < entries.size(); i++) {
            Entry post = entries.get(i);
            HashMap<String, String> blogPost = new HashMap<String, String>();
            blogPost.put(KEY_TITLE, post.title);
            blogPost.put(KEY_AUTHOR, "");
            blogPost.put(KEY_URL, post.link);
            blogPosts.add(blogPost);
        }
    }

Pretty easy, right?

I changed the type for mBlogPosts:

protected ArrayList<HashMap<String, String>> mBlogPosts;

I also moved the processing of mBlogPosts to the GetBlogPostsTask from handleBlogResponse() and onListItemClick(). This changes the well thought out design more than I would have liked (sorry Ben), but doing so allowed me to reuse StackOverflowParseXml as is.

Here is the code (excluding the imports for brevity):

package com.teamtreehouse.blogreader;

public class MainListActivity extends ListActivity {

    public static final int NUMBER_OF_POSTS = 20;
    public static final String TAG = MainListActivity.class.getSimpleName();
    protected ArrayList<HashMap<String, String>> mBlogPosts;
    protected ProgressBar mProgressBar;
    protected int mBlogType;

    private final String KEY_TITLE = "title";
    private final String KEY_AUTHOR = "author";
    private final String KEY_URL = "url";

    private final int BLOG_TYPE_TREEHOUSE_JSON = 0;
    private final int BLOG_TYPE_STACK_OVERFLOW_XML = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_list);

        mBlogPosts = null;
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
        mBlogType = BLOG_TYPE_STACK_OVERFLOW_XML;

        if (isNetworkAvailable()) {
            mProgressBar.setVisibility(View.VISIBLE);
            GetBlogPostsTask getBlogPostsTask = new GetBlogPostsTask();
            getBlogPostsTask.execute();
        }
        else {
            Toast.makeText(this, "Network is unavailable!", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        HashMap<String, String> blogPost = mBlogPosts.get(position);
        String blogUrl = blogPost.get(KEY_URL);

        Intent intent = new Intent(this, BlogWebViewActivity.class);
        intent.setData(Uri.parse(blogUrl));
        startActivity(intent);
    }

    private void logException(Exception e) {
        Log.e(TAG, "Exception caught!", e);
    }

    private boolean isNetworkAvailable() {
        ConnectivityManager manager = (ConnectivityManager) 
                getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();

        boolean isAvailable = false;
        if (networkInfo != null && networkInfo.isConnected()) {
            isAvailable = true;
        }

        return isAvailable;
    }

    public void handleBlogResponse() {
        mProgressBar.setVisibility(View.INVISIBLE);

        if (mBlogPosts == null) {
            updateDisplayForError();
        }
        else {
            String[] keys = { KEY_TITLE, KEY_AUTHOR };
            int[] ids = { android.R.id.text1, android.R.id.text2 };
            SimpleAdapter adapter = new SimpleAdapter(this, mBlogPosts,
                    android.R.layout.simple_list_item_2, 
                    keys, ids);

            setListAdapter(adapter);
        }
    }

    private void updateDisplayForError() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(getString(R.string.error_title));
        builder.setMessage(getString(R.string.error_message));
        builder.setPositiveButton(android.R.string.ok, null);
        AlertDialog dialog = builder.create();
        dialog.show();

        TextView emptyTextView = (TextView) getListView().getEmptyView();
        emptyTextView.setText(getString(R.string.no_items));
    }

    private class GetBlogPostsTask extends AsyncTask<String, Void, ArrayList<HashMap<String, String>>> {

        @Override
        protected ArrayList<HashMap<String, String>> doInBackground(String... urls) {
            int responseCode = -1;
            ArrayList<HashMap<String, String>> blogPosts = new ArrayList<HashMap<String, String>>();

            try {
                String url = "";
                switch (mBlogType) {
                case BLOG_TYPE_TREEHOUSE_JSON:      
                    url = "http://blog.teamtreehouse.com/api/get_recent_summary/?count=" + NUMBER_OF_POSTS;
                    break;
                case BLOG_TYPE_STACK_OVERFLOW_XML:
                    url = "http://stackoverflow.com/feeds/tag?tagnames=android&sort=newest&count=" + NUMBER_OF_POSTS;
                    break;
                }
                URL blogFeedUrl = new URL(url);
                HttpURLConnection connection = (HttpURLConnection) blogFeedUrl.openConnection();
                connection.connect();

                responseCode = connection.getResponseCode();
                if (responseCode == HttpURLConnection.HTTP_OK) {                    
                    switch (mBlogType) {
                    case BLOG_TYPE_TREEHOUSE_JSON:
                        extractBlogPostsTeamTreehouseJson(blogPosts, connection);
                        break;
                    case BLOG_TYPE_STACK_OVERFLOW_XML:
                        extractBlogPostsStackOverflowXml(blogPosts, connection);
                        break;
                    }
                }
                else {
                    Log.i(TAG, "Unsuccessful HTTP Response Code: " + responseCode);
                }
            }
            catch (MalformedURLException e) {
                logException(e);
            }
            catch (IOException e) {
                logException(e);
            }
            catch (JSONException e) {
                logException(e);
            }
            catch (Exception e) {
                logException(e);
            }

            return blogPosts;
        }

        protected void extractBlogPostsStackOverflowXml(ArrayList<HashMap<String, String>> blogPosts, HttpURLConnection connection) throws IOException, XmlPullParserException {
            StackOverflowXmlParser stackOverflowXmlParser = new StackOverflowXmlParser();
            InputStream inputStream = connection.getInputStream();
            List<Entry> entries = stackOverflowXmlParser.parse(inputStream);
            for (int i = 0; i < entries.size(); i++) {
                Entry post = entries.get(i);
                HashMap<String, String> blogPost = new HashMap<String, String>();
                blogPost.put(KEY_TITLE, post.title);
                blogPost.put(KEY_AUTHOR, "");
                blogPost.put(KEY_URL, post.link);

                blogPosts.add(blogPost);
            }
        }

        protected void extractBlogPostsTeamTreehouseJson(ArrayList<HashMap<String, String>> blogPosts, HttpURLConnection connection) throws JSONException, IOException {
            InputStream inputStream = connection.getInputStream();
            Reader reader = new InputStreamReader(inputStream);
            int contentLength = connection.getContentLength();
            char[] charArray = new char[contentLength];
            reader.read(charArray);
            String responseData = new String(charArray);
            JSONObject jsonResponse = new JSONObject(responseData);
            JSONArray jsonPosts = jsonResponse.getJSONArray("posts");               
            for (int i = 0; i < jsonPosts.length(); i++) {
                JSONObject post = jsonPosts.getJSONObject(i);
                String title = post.getString(KEY_TITLE);
                title = Html.fromHtml(title).toString();
                String author = post.getString(KEY_AUTHOR);
                author = Html.fromHtml(author).toString();
                String url = post.getString(KEY_URL);
                url = Html.fromHtml(url).toString();

                HashMap<String, String> blogPost = new HashMap<String, String>();
                blogPost.put(KEY_TITLE, title);
                blogPost.put(KEY_AUTHOR, author);
                blogPost.put(KEY_URL, url);

                blogPosts.add(blogPost);
            }
        }

        @Override
        protected void onPostExecute(ArrayList<HashMap<String, String>> blogPosts) {
            mBlogPosts = blogPosts;
            handleBlogResponse();
        }       
    }    
}
John Coffin
John Coffin
10,359 Points

Parsing XML data would not be much different from parsing JSON data. If your website doesn't have a JSON setting, you might do well to read over Android's Developer documentation on Parsing XML Data. There's an example, and it is the same concept.

I'll try this a bit later and will post my results if I am successful.

If you're using a CMS like WordPress, I think there are plugins which allow you to generate a JSON feed output.

Boyan Angelov
Boyan Angelov
26,485 Points

Thanks for the answers! Yes, I'm going to check the android dev docs. By the way I am using Jekyll for the site, and found something on converting to JSON there too, gonna give it a try as well.

Boyan Angelov
Boyan Angelov
26,485 Points

Thanks a lot John! It's so nice to have such people here on the forum. Saved me so much time :)

John Coffin
John Coffin
10,359 Points

The pleasure is mine. I have a project that will require processing a feed of sorts, so if we do go with XML -- I am now a BIG fan of using JSON, but the back-end people may feel differently -- then I will have a grasp of how to go about doing it and what extra work would be required.