簡體   English   中英

Json 使用 Retrofit 和 moshi -Android 解析失敗

[英]Json Parsing failed using Retrofit and moshi -Android

這是我得到的錯誤(這是一個次要錯誤,當我將數據綁定與 ListItemView 一起使用時我得到了這個)

    import com.example.bookkotlin.databinding.ListViewItemBindingImpl;
                                         ^
  symbol:   class ListViewItemBindingImpl
  location: package com.example.bookkotlin.databinding
> Task :app:kaptDebugKotlin FAILED
Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution
   > java.lang.reflect.InvocationTargetException (no error message)

build.gradle(項目:應用程序)

 buildscript {
    ext {
        kotlin_version = "1.4.20"
        version_glide = "4.8.0"
        version_lifecycle = "2.2.0"
        version_moshi = "1.9.3"
        version_navigation = "1.0.0"
        version_retrofit = "2.9.0"
        version_recyclerview = "1.2.0-alpha05"
    }
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.1.1"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

build.gradle(模塊:應用程序)

  plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id  'kotlin-parcelize'
    id 'kotlin-kapt'
}


android {

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    compileSdkVersion 30
    buildFeatures {
        dataBinding true
    }
    buildToolsVersion "30.0.1"

    defaultConfig {
        applicationId "com.example.bookkotlin"
        minSdkVersion 22
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    // Glide
    implementation "com.github.bumptech.glide:glide:$version_glide"

    // RecyclerView
    implementation "androidx.recyclerview:recyclerview:$version_recyclerview"

    // Retrofit with Moshi Converter
    implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
    // Moshi
    implementation "com.squareup.moshi:moshi:$version_moshi"
    implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"

    // ViewModel and LiveData
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$version_lifecycle"

    // Navigation
    implementation "android.arch.navigation:navigation-fragment-ktx:$version_navigation"
    implementation "android.arch.navigation:navigation-ui-ktx:$version_navigation"

    implementation "org.jetbrains.kotlin:kotlin-reflect:1.1.0"
}

這是 BookApiService

    package com.example.bookkotlin.network

import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query

private const val BASE_URL = "https://www.googleapis.com/books/"

private val moshi = Moshi.Builder()
        .add(KotlinJsonAdapterFactory())
        .build()

private val retrofit = Retrofit.Builder()
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .baseUrl(BASE_URL)
        .build()

interface BookApiService {

    @GET("v1/volumes")
    suspend fun getProperties(@Query("q") type : String) : BookResponse
}

object BookApi {
    val retrofitService : BookApiService by lazy { retrofit.create(BookApiService::class.java) }
}

這是BookResponse

      package com.example.bookkotlin.network
    
    import android.os.Parcelable
    import kotlinx.android.parcel.Parcelize
    
       @Parcelize
data class BookResponse( val items : Array<Items> ) : Parcelable

@Parcelize
data class Items (val id: String,
                  val volumeInfo : BookProperty) : Parcelable

@Parcelize
data class BookProperty( val authors: Array<String>?, val title: String,
                         val imageLinks: ImageLink) : Parcelable

@Parcelize
data class ImageLink( val smallThumbnail: String) : Parcelable

這是OverviewFragment

  package com.example.bookkotlin.overview

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.example.bookkotlin.R
import com.example.bookkotlin.databinding.FragmentOverviewBinding


class OverviewFragment : Fragment() {

    private val viewModel: OverviewViewModel   by lazy {
        ViewModelProvider(this).get(OverviewViewModel::class.java)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?): View? {


     val binding: FragmentOverviewBinding = DataBindingUtil.inflate(
            inflater, R.layout.fragment_overview, container, false)

        binding.lifecycleOwner = this

        binding.viewModel = viewModel
       binding.recyclerList.adapter = RecyclerviewAdapter()

        return binding.root
    }
}

我從我的 OverviewViewModel 調用 API

    package com.example.bookkotlin.overview

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.bookkotlin.network.BookApi
import com.example.bookkotlin.network.BookResponse
import kotlinx.coroutines.launch

class OverviewViewModel : ViewModel() {

    // Internally, we use a MutableLiveData, because we will be updating the List of MarsProperty
    // with new values
    private val _properties = MutableLiveData<BookResponse> ()

    // The external LiveData interface to the property is immutable, so only this class can modify
    val properties: LiveData<BookResponse>
        get() = _properties

    init {
        getBookProperties()
    }

    private fun getBookProperties() {
        viewModelScope.launch {
            try {
                _properties.value =BookApi.retrofitService.getProperties("Harry Potter")
            } catch (e : Exception) {

            }
        }
    }
}

RecyclerviewAdapter

  package com.example.bookkotlin.overview

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.bookkotlin.databinding.ListViewItemBinding
import com.example.bookkotlin.network.Items


class RecyclerviewAdapter() : ListAdapter<Items, RecyclerviewAdapter.BookPropertyViewHolder>(DiffCallback()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookPropertyViewHolder {
        return BookPropertyViewHolder.from(parent)
    }

    override fun onBindViewHolder(holder: BookPropertyViewHolder, position: Int) {
    
       val item = getItem(position)
        holder.bind(item)
    }

    class BookPropertyViewHolder private constructor(val binding: ListViewItemBinding): RecyclerView.ViewHolder(binding.root) {

       fun bind(books: Items) {
            binding.property = books
           binding.executePendingBindings()
        }

        companion object {
            fun from(parent: ViewGroup): BookPropertyViewHolder {
                val layoutInflater = LayoutInflater.from(parent.context)
                val binding = ListViewItemBinding.inflate(layoutInflater, parent, false)
                return BookPropertyViewHolder(binding)
            }
        }
    }
}

class  DiffCallback : DiffUtil.ItemCallback<Items>() {
    override fun areItemsTheSame(oldItem: Items, newItem: Items): Boolean {
        return oldItem === newItem
    }

    override fun areContentsTheSame(oldItem: Items, newItem: Items): Boolean {
        return oldItem.volumeInfo.id == newItem.volumeInfo.id

    }
}

綁定適配器

  package com.example.bookkotlin

import android.widget.ImageView
import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.bookkotlin.network.Items
import com.example.bookkotlin.overview.RecyclerviewAdapter


@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: BookResponse) {
    val adapter = recyclerView.adapter as RecyclerviewAdapter
    adapter.submitList(data.items.toList())
}
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String) {
        Glide.with(imgView)
                .load(imgUrl)
                .into(imgView)

}

這是 list_view_item

    <?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto" >

    <data>
        <variable
            name="property"
            type="com.example.bookkotlin.network.Items" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="150dp"
        android:orientation="horizontal"
        android:paddingStart="@dimen/margin_16_dp"
        android:paddingLeft="@dimen/margin_16_dp"
        android:paddingEnd="@dimen/margin_16_dp"
        android:paddingRight="@dimen/margin_16_dp"
        android:id="@+id/container">
        
        <ImageView
            android:id="@+id/cover_image"
            android:layout_width="@dimen/image_view_width"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:scaleType="centerInside"
            tools:src="@mipmap/ic_launcher"
            app:imageUrl ="@{property.volumeInfo.imageLinks.smallThumbnail}"/>

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginStart="@dimen/margin_16_dp"
            android:layout_marginLeft="@dimen/margin_16_dp"
            android:layout_weight="1"
            android:orientation="vertical">

            <TextView
                android:id="@+id/author"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:fontFamily="sans-serif-medium"
                android:maxLines="1"
                android:textAllCaps="true"
                android:textColor="@color/textColorAuthor"
                android:textSize="@dimen/author_text_size"
                android:text="@{property.volumeInfo.authors.toString()}" />

            <TextView
                android:id="@+id/book_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:elegantTextHeight="true"
                android:ellipsize="end"
                android:lineSpacingMultiplier="1.1"
                android:maxLines="2"
                android:textColor="@color/textColorBookTitle"
                android:textSize="@dimen/title_text_size"
                android:text="@{property.volumeInfo.title}"
                />

        </LinearLayout>


    </LinearLayout>
</layout>

片段概述

 <?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="viewModel"
            type="com.example.bookkotlin.overview.OverviewViewModel" />
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context = "com.example.bookkotlin.MainActivity">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <LinearLayout
                android:id="@+id/searchSection"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginRight="8dp"
                android:orientation="horizontal">

                <SearchView
                    android:id="@+id/search_view_edit_text"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_weight="1"
                    android:iconifiedByDefault="false"
                    android:queryHint="Enter a Book Title"
                    tools:queryHint="Enter a Title" />

                <Button
                    android:id="@+id/search_book_button"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Search" />
            </LinearLayout>

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycler_list"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical"
                app:listData="@{viewModel.properties}"
                tools:listitem="@layout/list_view_item"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
            
        </LinearLayout>
        <TextView
            android:id="@+id/empty_view"
            android:visibility="invisible"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textAppearance="?android:textAppearanceMedium" />

        <ProgressBar
            android:id="@+id/loading_progress"
            style="?android:progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true" />
        
    </RelativeLayout>
</layout>

請幫我修復錯誤,我還是 Kotlin 編程的新手。

我在解析 kotlin 中的 Json 並在需要的地方將List<ObjectType>替換為Array<ObjectType>時遇到了類似的問題。

嘗試以下

    @Parcelize
    data class BookResponse( val items : Array<Items> ) : Parcelable
    
      @Parcelize 
     data class Items (val volumeInfo : BookProperty) : Parcelable
    
    
// the volumeinfo returns a JsonObject and that object doesn't contain an **id** field, so remove the **id** member variable from BookProperty
    @Parcelize
    data class BookProperty( val authors: Array<String>?, val title: String, 
   val imageLinks: ImageLink
        ) : Parcelable
    
    @Parcelize
    data class ImageLink( val smallThumbnail: String) : Parcelable

此外,API 僅返回一個 object 而不是列表,因此也進行以下更改

interface BookApiService {

    @GET("v1/volumes")
    suspend fun getProperties(@Query("q") type : String) : BookResponse
}

編輯:

你這個格式化程序來可視化 Json 響應。 例如,當您查詢Harry Potter的 API 時,您將得到一個Object 而不是對象列表。

概覽視圖模型

//change the datatype to hold a single object instead of a list

 private val _properties = MutableLiveData<BookResponse>()

 val properties: LiveData<BookResponse>
        get() = _properties

BindingUtils

@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: BookResponse) {
    val adapter = recyclerView.adapter as RecyclerviewAdapter
    adapter.submitList(data.items.toList())
}

項目

@Parcelize
data class Items(val volumeInfo: BookProperty, val id: String) : Parcelable

差異回調

class DiffCallback : DiffUtil.ItemCallback<Items>() {
    override fun areItemsTheSame(oldItem: Items, newItem: Items): Boolean {
        return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: Items, newItem: Items): Boolean {
        return oldItem == newItem
    }
}

數據解析成功

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM