Vid Bregar
Vid Bregar

Reputation: 148

Parsing JSON Objects with Different Keys

I created an Android application which uses the Google Books API. When I get the JSON response from the server, I parse the response and retrieve the title, authors, category, publisher, page count, thumbnail and some more information about the book. The problem is that some books in the JSON response don't have the thumbnail key or category key. When I try to get those JSON key values the program throws an error and consequently skips the code of adding other books after the error occurred.

I solved that with nested try catch blocks. For example, if there isn't a publisher key in the response, then I would return null.

String publisher;
  try {
    publisher = volumeInfo.getString("publisher");
  } catch (JSONException e) {
    publisher = null;
  }

Here is how the whole method for parsing the JSON response looks like:

private List<BookData> parseJsonResponse(String jsonResponse) {
    List<BookData> bookData = new ArrayList<>();

    try {
        JSONObject rootObject = new JSONObject(jsonResponse);
        JSONArray itemsArray = rootObject.getJSONArray("items");
        for (int i = 0; i < itemsArray.length(); i++) {
            JSONObject itemObject = itemsArray.getJSONObject(i);
            JSONObject volumeInfo = 
              itemObject.getJSONObject("volumeInfo");

            String title;
            try {
                title = volumeInfo.getString("title");
            } catch (JSONException e) {
                title = null;
            }

            ArrayList<String> authors;
            try {
                JSONArray authorsArray = 
                  volumeInfo.getJSONArray("authors");
                authors = new ArrayList<>();
                for (int j = 0; j < authorsArray.length(); j++) {
                    authors.add(authorsArray.getString(j));
                }
            } catch (JSONException e) {
                authors = null;
            }

            ArrayList<String> categories;
            try {
                JSONArray categoriesArray = 
                  volumeInfo.getJSONArray("categories");
                categories = new ArrayList<>();
                for (int k = 0; k < categoriesArray.length(); k++) {
                    categories.add(categoriesArray.getString(k));
                }
            } catch (JSONException e) {
                categories = null;
            }

            String publisher;
            try {
                publisher = volumeInfo.getString("publisher");
            } catch (JSONException e) {
                publisher = null;
            }

            String publishedDate;
            try {
                publishedDate = 
                  volumeInfo.getString("publishedDate");
            } catch (JSONException e) {
                publishedDate = null;
            }

            int pageCount;
            try {
                pageCount = volumeInfo.getInt("pageCount");
            } catch (JSONException e) {
                pageCount = 0;
            }

            String language;
            try {
                language = volumeInfo.getString("language");
            } catch (JSONException e) {
                language = null;
            }

            String description;
            try {
                description = volumeInfo.getString("description");
            } catch (JSONException e) {
                description = null;
            }

            String bookWebsite;
            try {
                bookWebsite = volumeInfo.getString("infoLink");
            } catch (JSONException e) {
                bookWebsite = null;
            }

            Bitmap thumbnail;
            try {
                JSONObject imageLink = 
                  volumeInfo.getJSONObject("imageLinks");
                String thumbnailUrl = 
                  imageLink.getString("thumbnail");
                thumbnail = getThumbnailBitmap(thumbnailUrl);
            } catch (JSONException e) {
                thumbnail = null;
            }

            // Add a new BookData object to the list
            bookData.add(new BookData(title, thumbnail, authors, 
                       categories, publisher, publishedDate, 
                       pageCount, language, description,
                       bookWebsite));
        }
    } catch (JSONException e) {
        Log.e(LOG_TAG, null, e);
    }
    return bookData;
}

After I complete my parsing, I have to update my views. I am using a list view, so the adapter needs to handle the views inflation. I had to add an if statement to check if the variable is not null, then for example set the text of the text view. Else I set the text to "Publisher not available".

TextView publisher = listView.findViewById(R.id.book_publisher);
  if (bookData.getPublisher() != null) {
    publisher.setText(bookData.getPublisher());
    } else {
    publisher.setText("Publisher not available");
    }

Here is how the whole adapter looks like:

public class BookDataAdapter extends ArrayAdapter<BookData> {

public BookDataAdapter(@NonNull Context context, @NonNull 
                        List<BookData> bookDatas) {
    super(context, 0, bookDatas);
}

@NonNull
@Override
public View getView(int position, @Nullable View convertView, 
                     @NonNull ViewGroup parent) {
    View listView = convertView;
    if (listView == null) {
        listView = LayoutInflater.from(getContext())
                .inflate(R.layout.book_list_item, parent, false);
    }
    // Get current BookData object
    BookData bookData = getItem(position);

    ImageView thumbnail = listView.findViewById(R.id.book_thumbnail);
    if (bookData.getThumbnail() != null) {
        thumbnail.setImageBitmap(bookData.getThumbnail());
    } else {
        // Set default thumbnail
        thumbnail.setImageResource(R.drawable.default_thumbnail);
    }

    TextView title = listView.findViewById(R.id.book_title);
    if (bookData.getTitle() != null) {
        title.setText(bookData.getTitle());
    } else {
        title.setText("Title not available");
    }

    TextView author = listView.findViewById(R.id.book_author);
    if (bookData.getAuthors() != null) {
        author.setText(listToString(bookData.getAuthors()));
    } else {
        author.setText("Authors not available");
    }

    TextView category = listView.findViewById(R.id.book_category);
    if (bookData.getCategories() != null) {
        category.setText("Category: " + 
      listToString(bookData.getCategories()));
    } else {
        category.setText("Category not available ");
    }

    TextView publisher = listView.findViewById(R.id.book_publisher);
    if (bookData.getPublisher() != null) {
        publisher.setText(bookData.getPublisher() + ", ");
    } else {
        publisher.setText("Publisher not available, ");
    }

    TextView publishedDate = 
      listView.findViewById(R.id.book_published_date);
    if (bookData.getPublishedDate() != null) {
        publishedDate.setText(bookData.getPublishedDate());
    } else {
        publishedDate.setText("Published date not available");
    }

    TextView pageCount = listView.findViewById(R.id.book_page_count);
    if (bookData.getPageCount() != 0) {
        pageCount.setText("Pages: " + bookData.getPageCount());
    } else {
        pageCount.setText("Page count not available");
    }

    TextView language = listView.findViewById(R.id.book_language);
    if (bookData.getLanguage() != null) {
        language.setText(bookData.getLanguage());
    } else {
        language.setText("Language not available");
    }

    TextView description = 
      listView.findViewById(R.id.book_description);
    if (bookData.getDescription() != null) {
        description.setText(bookData.getDescription());
    } else {
        description.setText("Description not available");
    }
    return listView;
}

private String listToString(List<String> list) {
    if (list == null || list.size() == 0) {
        return null;
    }
    StringBuilder builder = new StringBuilder();
    for (int i = 0; i < list.size(); i++) {
        builder.append(list.get(i));
        if (i == (list.size() - 1)) {
            break;
        }
        builder.append(", ");
    }
    return builder.toString();
}

}

And lastly I want to ask a question. Is there a better way or more efficient way of parsing the JSON response with different keys, because some people say that nested try catch statements are not a good practice?

Thank you very much!!

Upvotes: 2

Views: 160

Answers (3)

BackSlash
BackSlash

Reputation: 22243

You have two options:

  1. Using .has():

    String publisher = null;
    if(volumeInfo.has("publisher")){
        publisher = volumeInfo.getString("publisher");
    }
    
  2. Using opt instead of get (better, IMO):

    String publisher = volumeInfo.optString("publisher");
    

opt### methods default to null for objects and 0/false for primitives, so you don't have to write try/catch blocks or if conditions. You can also specify a second parameter as default value:

String publisher = volumeInfo.optString("publisher", "no publisher");
// if publisher is not a valid key, "no publisher" will be returned

Upvotes: 1

anomeric
anomeric

Reputation: 698

You don't need to wrap json operations in individual try/catch blocks.

There is a method in the json library to handle this problem:

jsonObject.isNull(key);

When you attempt to grab a value by key write it like this:

if (!volumeInfo.isNull("categories")) {
    JSONArray categoryArray = volumeInfo.getJSONArray("categories");
}

Upvotes: 0

Nilesh Deokar
Nilesh Deokar

Reputation: 2985

Use can you .has() property of JSONObject

if(volumeInfo.has("publisher")){
   volumeInfo.getString("publisher");
}

Upvotes: 0

Related Questions