Android 如何使用reformation/Moshi解析嵌套的JSON对象
我使用这个CodeLabs教程来学习如何从GoogleBooksAPI发出HTTP请求 现在,我正在尝试访问GoogleBooksAPI吐出的嵌套JSON对象 即 我只需要描述和缩略图属性 我的API服务接口是Android 如何使用reformation/Moshi解析嵌套的JSON对象,android,kotlin,retrofit2,moshi,Android,Kotlin,Retrofit2,Moshi,我使用这个CodeLabs教程来学习如何从GoogleBooksAPI发出HTTP请求 现在,我正在尝试访问GoogleBooksAPI吐出的嵌套JSON对象 即 我只需要描述和缩略图属性 我的API服务接口是 package com.example.customapp.network import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import
package com.example.customapp.network
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
//Code from https://codelabs.developers.google.com/codelabs/kotlin-android-training-internet-data/#3
private const val BASE_URL = "https://www.googleapis.com/books/v1/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface BookApiService {
//Get annotation specifies the endpoint for this web service method.
//when getProperties() method is invoked, Retrofit appends the endpoint 'book' to the base URL
//And creates a Call object. The Call object is used to start the request.
@GET("volumes?q='harry+potter")
suspend fun getProperties(): BookProperty
}
object BookApi {
val retrofitService: BookApiService by lazy {
retrofit.create(BookApiService::class.java)
}
}
}
我的BookProperty.kt是
data class BookProperty(@field:Json(name = "items" ) val bookDetail: List<BookDetail>)
data class BookDetail(@field:Json(name = "volumeInfo") val volumeInfo: VolumeInfo)
data class VolumeInfo(@field:Json(name = "description") val description: String, @field:Json(name= "imageLinks") val imageLink: ImageLink)
data class ImageLink(@field:Json(name = "thumbnail") val thumbnail: String)
数据类BookProperty(@field:Json(name=“items”)val bookDetail:List)
数据类BookDetail(@field:Json(name=“volumeInfo”)val volumeInfo:volumeInfo)
数据类VolumeInfo(@field:Json(name=“description”)val description:String,@field:Json(name=“imageLinks”)val-imageLink:imageLink)
数据类ImageLink(@field:Json(name=“thumbnail”)val thumbnail:String)
我正在从ViewModel调用API
val readAllData: LiveData<List<BookItem>>
private val repository: BookRepository
private val _response = MutableLiveData<String>()
val response: LiveData<String>
get() = _response
init {
val bookDao = BookDatabase.getDatabase(application).bookDao()
repository = BookRepository(bookDao)
readAllData = repository.readAllData
}
fun addBook(book: BookItem) {
viewModelScope.launch(Dispatchers.IO) {
repository.addBook(book)
}
}
fun updateBook(book: BookItem) {
viewModelScope.launch(Dispatchers.IO) {
repository.updateBook(book)
}
}
fun getBookDetailProperties() {
viewModelScope.launch {
try {
//calling get properties from the BookApi service creates and starts the network call
//on a background thread
var listResult = BookApi.retrofitService.getProperties()
_response.value = "${
listResult.bookDetail[0].volumeInfo.description} book properties received"
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
}
}
val readAllData:LiveData
私有val存储库:BookRepository
private val_response=MutableLiveData()
val响应:LiveData
get()=\u响应
初始化{
val bookDao=BookDatabase.getDatabase(应用程序).bookDao()
repository=BookRepository(bookDao)
readAllData=repository.readAllData
}
趣味addBook(书籍:BookItem){
viewModelScope.launch(Dispatchers.IO){
repository.addBook(book)
}
}
趣味更新电子书(书籍:BookItem){
viewModelScope.launch(Dispatchers.IO){
repository.updateBook(书籍)
}
}
有趣的getBookDetailProperties(){
viewModelScope.launch{
试一试{
//从BookApi服务调用get属性将创建并启动网络调用
//在背景线程上
var listResult=BookApi.reformationservice.getProperties()
_response.value=”${
listResult.bookDetail[0].volumeInfo.description}已收到书本属性“
}捕获(e:例外){
_response.value=“失败:${e.message}”
}
}
}
每次我在CRUD应用程序上更新一个项目时,即当我点击一个按钮时,我都试图发出一个HTTP请求,但我似乎无法得到任何回复。这是我发起API调用的UpdateFragment
class UpdateFragment : Fragment() {
//Read up on delegation
//https://codelabs.developers.google.com/codelabs/kotlin-bootcamp-classes/#7
//UpdateFragmentArgs is a class that is automatically generated
//when we created an argument for our Update Fragment in the nav graph
//UpdateFragmentArgs will contain our current book
//we can also use bundle
private val args by navArgs<UpdateFragmentArgs>()
private lateinit var mBookViewModel: BookViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_update, container, false)
//So the keyboard doesn't push the EditText fields up
this.activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
Glide
.with(this)
.load(args.currentBook.image)
.into(view.bookImageDetail)
mBookViewModel = ViewModelProvider(this).get(BookViewModel::class.java)
view.apply {
updateInputName.setText(args.currentBook.title)
updateInputAuthor.setText(args.currentBook.author)
updateBookDesc.text = args.currentBook.desc
updateRatingBar.rating = args.currentBook.rating.toFloat()
updateBookCompleted.isChecked = args.currentBook.finished
updateBookCompleted.text =
if (updateBookCompleted.isChecked) getString(R.string.book_completed) else getString(
R.string.book_not_completed
)
updateDateCreated.text = getString(R.string.date_created, args.currentBook.dateCreated)
}
view.updateBtn.setOnClickListener {
updateItem()
}
view.updateBookCompleted.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
view.updateBookCompleted.text = getString(R.string.book_completed)
} else {
view.updateBookCompleted.text = getString(R.string.book_not_completed)
}
}
return view
}
private fun updateItem() {
val bookName = updateInputName.text.toString()
val bookAuthor = updateInputAuthor.text.toString()
val bookRating = updateRatingBar.rating.toDouble()
val bookFinished = updateBookCompleted.isChecked
if (inputCheck(bookName, bookAuthor)) {
//***Initiate API call here ****
mBookViewModel.getBookDetailProperties()
//Get description and image from API
mBookViewModel.response.observe(viewLifecycleOwner, {
println("Get resp " + it)
})
//Create book object
val updatedBook = BookItem(
args.currentBook.id,
bookName,
bookAuthor,
args.currentBook.desc,
args.currentBook.image,
bookRating,
args.currentBook.dateCreated,
bookFinished
)
//update current book
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(), "Updated book successfully!", Toast.LENGTH_SHORT)
.show()
//navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
}
}
private fun inputCheck(bookName: String, authorName: String): Boolean {
return !(TextUtils.isEmpty(bookName) && TextUtils.isEmpty(authorName))
}
}
类UpdateFragment:Fragment(){
//阅读授权书
//https://codelabs.developers.google.com/codelabs/kotlin-bootcamp-classes/#7
//UpdateFragmentArgs是一个自动生成的类
//当我们在导航图中为更新片段创建参数时
//UpdateFragmentArgs将包含我们当前的书籍
//我们也可以使用bundle
私有val args by navArgs()
私有lateinit变量mBookViewModel:BookViewModel
覆盖创建视图(
充气器:布局充气器,容器:视图组?,
savedInstanceState:捆绑?
):查看{
//为该碎片膨胀布局
val视图=充气机。充气(R.layout.fragment\u更新,容器,错误)
//因此键盘不会向上推EditText字段
此.activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT\u INPUT\u ADJUST\u PAN)
滑翔
.与(此)
.load(args.currentBook.image)
.into(view.bookImageDetail)
mBookViewModel=ViewModelProvider(this).get(BookViewModel::class.java)
view.apply{
updateInputName.setText(args.currentBook.title)
updateInputAuthor.setText(args.currentBook.author)
updateBookDesc.text=args.currentBook.desc
updateRatingBar.rating=args.currentBook.rating.toFloat()
updateBookCompleted.isChecked=args.currentBook.finished
updateBookCompleted.text=
if(updateBookCompleted.isChecked)getString(R.string.book_completed)else getString(
R.string.book\u未完成
)
updateDateCreated.text=getString(R.string.date_created,args.currentBook.dateCreated)
}
view.updateBtn.setOnClickListener{
updateItem()
}
view.updateBookCompleted.setOnCheckedChangeListener{},isChecked->
如果(已检查){
view.updateBookCompleted.text=getString(R.string.book\u已完成)
}否则{
view.updateBookCompleted.text=getString(R.string.book\u未完成)
}
}
返回视图
}
私人娱乐更新项目(){
val bookName=updateInputName.text.toString()
val bookAuthor=updateInputAuthor.text.toString()
val bookRating=updateRatingBar.rating.toDouble()
val bookFinished=updateBookCompleted.isChecked
if(输入检查(书名、书籍作者)){
//***在此处启动API调用****
mBookViewModel.getBookDetailProperties()
//从API获取描述和图像
mBookViewModel.response.observe(viewLifecycleOwner{
println(“获取响应”+它)
})
//创建图书对象
val updatedBook=BookItem(
args.currentBook.id,
书名,
作者:,
args.currentBook.desc,
args.currentBook.image,
簿记评级,
args.currentBook.dateCreated,
书页
)
//更新当前书籍
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(),“已成功更新书籍!”,Toast.LENGTH\u SHORT)
.show()
//向后导航
findNavController().导航(R.id.action\u updateFragment\u到\u listFragment)
}
}
private fun inputCheck(书名:String,authorName:String):布尔值{
return!(TextUtils.isEmpty(书名)&&TextUtils.isEmpty(authorName))
}
}
问题是我无法从API调用中获得任何响应-我不确定这是否是因为JSON中的嵌套对象。请帮帮我
class UpdateFragment : Fragment() {
//Read up on delegation
//https://codelabs.developers.google.com/codelabs/kotlin-bootcamp-classes/#7
//UpdateFragmentArgs is a class that is automatically generated
//when we created an argument for our Update Fragment in the nav graph
//UpdateFragmentArgs will contain our current book
//we can also use bundle
private val args by navArgs<UpdateFragmentArgs>()
private lateinit var mBookViewModel: BookViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_update, container, false)
//So the keyboard doesn't push the EditText fields up
this.activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
Glide
.with(this)
.load(args.currentBook.image)
.into(view.bookImageDetail)
mBookViewModel = ViewModelProvider(this).get(BookViewModel::class.java)
view.apply {
updateInputName.setText(args.currentBook.title)
updateInputAuthor.setText(args.currentBook.author)
updateBookDesc.text = args.currentBook.desc
updateRatingBar.rating = args.currentBook.rating.toFloat()
updateBookCompleted.isChecked = args.currentBook.finished
updateBookCompleted.text =
if (updateBookCompleted.isChecked) getString(R.string.book_completed) else getString(
R.string.book_not_completed
)
updateDateCreated.text = getString(R.string.date_created, args.currentBook.dateCreated)
}
view.updateBtn.setOnClickListener {
updateItem()
}
view.updateBookCompleted.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
view.updateBookCompleted.text = getString(R.string.book_completed)
} else {
view.updateBookCompleted.text = getString(R.string.book_not_completed)
}
}
return view
}
private fun updateItem() {
val bookName = updateInputName.text.toString()
val bookAuthor = updateInputAuthor.text.toString()
val bookRating = updateRatingBar.rating.toDouble()
val bookFinished = updateBookCompleted.isChecked
if (inputCheck(bookName, bookAuthor)) {
//***Initiate API call here ****
mBookViewModel.getBookDetailProperties()
//Get description and image from API
mBookViewModel.response.observe(viewLifecycleOwner, {
println("Get resp " + it)
})
//Create book object
val updatedBook = BookItem(
args.currentBook.id,
bookName,
bookAuthor,
args.currentBook.desc,
args.currentBook.image,
bookRating,
args.currentBook.dateCreated,
bookFinished
)
//update current book
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(), "Updated book successfully!", Toast.LENGTH_SHORT)
.show()
//navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
}
}
private fun inputCheck(bookName: String, authorName: String): Boolean {
return !(TextUtils.isEmpty(bookName) && TextUtils.isEmpty(authorName))
}
}
//Get description and image from API
mBookViewModel.response.observe(viewLifecycleOwner, {
println("Get resp " + it)
})
//Create book object
val updatedBook = BookItem(
args.currentBook.id,
bookName,
bookAuthor,
args.currentBook.desc,
args.currentBook.image,
bookRating,
args.currentBook.dateCreated,
bookFinished
)
//update current book
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(), "Updated book successfully!", Toast.LENGTH_SHORT)
.show()
//navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
//Get description and image from API
mBookViewModel.response.observe(viewLifecycleOwner, {
println("Get resp " + it)
//Create book object
val updatedBook = BookItem(
args.currentBook.id,
bookName,
bookAuthor,
args.currentBook.desc,
args.currentBook.image,
bookRating,
args.currentBook.dateCreated,
bookFinished
)
//update current book
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(), "Updated book successfully!", Toast.LENGTH_SHORT)
.show()
//navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
})