用不同的键解析JSON对象

问题描述:

我创建了一个使用Google Books API的Android应用程序。当我从服务器获得JSON响应时,我解析响应并检索标题,作者,类别,发布者,页数,缩略图和关于该书的更多信息。问题在于JSON响应中的某些书没有缩略图键或类别键。当我尝试获取这些JSON密钥值时,程序将引发错误,从而跳过发生错误后添加其他书籍的代码。用不同的键解析JSON对象

我解决了与嵌套尝试catch块。例如,如果在响应中没有发布者密钥,那么我会返回null。

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

下面是解析JSON响应的整体方法的样子:

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; 
} 

我完成了我的分析后,我需要更新我的看法。我正在使用列表视图,因此适配器需要处理视图通货膨胀。 我不得不添加一个if语句来检查变量是否为空,然后例如设置文本视图的文本。否则,我将文本设置为“发布者不可用”。

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

这里是整个适配器的样子:

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(); 
} 

}

而且最后我想问一个问题。有没有更好的方法或更有效的方法来解析不同的键的JSON响应,因为有人说嵌套try catch语句不是一个好习惯?

非常感谢!

你有两个选择:

  1. 使用.has()

    String publisher = null; 
    if(volumeInfo.has("publisher")){ 
        publisher = volumeInfo.getString("publisher"); 
    } 
    
  2. 使用opt,而不是get(更好,IMO):

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

opt###方式默认为null对象和原语0/false,所以你不必写try/catch区块或if条件。你也可以指定第二个参数作为默认值:

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

谢谢,这是一个更干净的方式来做我的解析。 –

+0

@VidBregar我也更喜欢第二种选择,它很干净并且不需要额外的代码。别客气! – BackSlash

可以使用你的JSONObject

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

.has()财产你不需要在各个try/catch块包裹JSON操作。

有一个在JSON库的方法来处理这个问题:当你试图抓住的关键写这样的值

jsonObject.isNull(key); 

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