【问题标题】:Parsing JSON Objects with Different Keys使用不同的键解析 JSON 对象
【发布时间】:2017-08-14 15:32:07
【问题描述】:

我创建了一个使用 Google Books API 的 Android 应用程序。当我从服务器获得 JSON 响应时,我会解析响应并检索书名、作者、类别、出版商、页数、缩略图和一些关于这本书的更多信息。问题是 JSON 响应中的某些书籍没有缩略图键或类别键。当我尝试获取这些 JSON 键值时,程序会抛出错误,因此会在错误发生后跳过添加其他书籍的代码。

我用 nested try 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 语句来检查变量是否不为空,然后例如设置文本视图的文本。否则我将文本设置为“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");
    }

这是整个适配器的样子:

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 语句不是一个好习惯?

非常感谢!!

【问题讨论】:

    标签: java android json


    【解决方案1】:

    你有两个选择:

    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
    

    【讨论】:

    • 谢谢,这是一种更简洁的解析方式。
    • @VidBregar 我也更喜欢第二个选项,它很干净,不需要额外的代码。不客气!
    【解决方案2】:

    你可以使用.has()JSONObject的属性

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

    【讨论】:

      【解决方案3】:

      您不需要将 json 操作包装在单独的 try/catch 块中。

      json库中有处理这个问题的方法:

      jsonObject.isNull(key);
      

      当您尝试通过键获取值时,请这样写:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-15
        • 1970-01-01
        相关资源
        最近更新 更多