无法使用Kotlin,Retrofit,RxJava和MVVM模式解析嵌套的JSON

问题描述 投票:0回答:3

在我的项目中,我遇到了一些有关在MVVM模式中使用Kotlin,Retrofit和RxJava返回嵌套JSON数据的问题。

我对RxJava,MVVM和Retrofit还是比较陌生,但仍然很困惑,但是最后我想得到一个当前嵌套在JSON中的选择类别列表。

JSON响应看起来像这样...

{
  "status": "ok",
  "totalResults": 38,
  "articles": [
    {
      "source": {
        "id": "business-insider",
        "name": "Business Insider"
      },
      "author": "Hayley Peterson",
      "title": "Amazon executive was killed after colliding with a van delivering the company's packages, report reveals - Business Insider",
      "description": "\"I heard a scream, immediately followed by a crash,\" the van's driver testified, according to the report.",
      "url": "https://www.businessinsider.com/amazons-joy-covey-killed-company-delivery-van-report-2019-12",
      "urlToImage": "https://image.businessinsider.com/5e01376e855cc215577a09f1?width=1200&format=jpeg",
      "publishedAt": "2019-12-23T22:32:01Z",
      "content": "The former Amazon executive Joy Covey was killed after colliding with a van delivering Amazon packages, according to an explosive investigation into the company's logistics network by BuzzFeed News and ProPublica. \r\nCovey was Amazon's first chief financial of… [+1462 chars]"
    },
...

我的数据类看起来像这样...

data class Base (

    @SerializedName("status") val status : String,
    @SerializedName("totalResults") val totalResults : Int,
    @SerializedName("articles") val articles : List<Story>
)

data class Story (

    @SerializedName("source") val source : Source,
    @SerializedName("author") val author : String,
    @SerializedName("title") val title : String,
    @SerializedName("description") val description : String,
    @SerializedName("url") val url : String,
    @SerializedName("urlToImage") val urlToImage : String,
    @SerializedName("publishedAt") val publishedAt : String,
    @SerializedName("content") val content : String
)

data class Source (

    @SerializedName("id") val id : String,
    @SerializedName("name") val name : String
)

我首先将我的API与@GET注释一起使用,以使此代码获得头条新闻...

interface StoriesApi {

    @GET("v2/top-headlines?country=us&apiKey=###MYKEY###")
    fun getStories(): Single<List<Story>>
}

我的StoriesService依次使用哪一个来获取Single>

class StoriesService {

    @Inject
    lateinit var api: StoriesApi

    init {
        DaggerApiComponent.create().inject(this)
    }

    fun getStories(): Single<List<Story>> {
        return api.getStories()
    }
}

最后,我使用下面的代码在ViewModel中调用它...

private fun fetchStories() {
        loading.value = true
        disposable.add(
            storiesService.getStories()
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeWith(object: DisposableSingleObserver<List<Story>>() {
                    override fun onSuccess(value: List<Story>?) {
                        stories.value = value
                        storyLoadError.value = false
                        loading.value = false
                    }

                    override fun onError(e: Throwable?) {
                        storyLoadError.value = true
                        loading.value = false
                    }

                })
        )
    }

有什么办法我只能输入JSON的文章部分,这样我就不必对整个JSON响应进行过多摆弄?我最终只想获得文章的Single>,而不是“ status”和“ totalResults”。

android kotlin mvvm rx-java retrofit2
3个回答
1
投票

您实际上似乎在这里没有使用Retrofit。这是在改造和协同程序中的操作方法-如果您不熟悉该主题,则可能需要研究协同程序:

data class ApiResultModel (

   val totalResults : Int
)

data class Story (

    val source : Source,
    val author : String,
    val title : String,
    val description : String,
    val url : String,
    val urlToImage : String,
    val publishedAt : String,
    val content : String
)

构建api接口:

interface StoriesApiService {

    @GET("top-headlines" - you insert just the end point here, the search parameters should go bellow because you might need to change them)
    fun getStories(country: String = us,
                   apiKey: String = {your api key}
                    ): Deferred<ApiResultModel>
}

然后创建一个包含所有这些的对象MyApi:

private val okHttpClient = OkHttpClient.Builder()
    .build()

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

private val retrofit = Retrofit.Builder()
    .addConverterFactory(MoshiConverterFactory.create(moshi))
    .addCallAdapterFactory(CoroutineCallAdapterFactory())
    .baseUrl({the base url of the API})
    .client(okHttpClient)
    .build()

object MyApi {
    val storiesApiService: StoriesApiService by lazy {
        retrofit.create(StoriesApiService::class.java)
    }
}

然后,为了访问来自API的数据,您将需要一个协程范围来进行调用:

private val job = Job()

private val coroutineScope = CoroutineScope(Job() + Dispatchers.Main)

coroutineScope.launch {
    val apiResult = MyApi.storiesApiService.getstories().await()
}

该代码可能仍然存在一些拼写错误,因此,如果您打算使用此方法进行API调用,请再次检查。

您还应该观看由Google开发人员创建的有关Udacity的免费教程。我使用了与他们相同的方法,但是他们有更多的解释。


0
投票

请尝试一下

interface StoriesApi {
    @GET("v2/top-headlines?country=us&apiKey=###MYKEY###")
    fun getStories(): Single<Story> /* change this */
}

class StoriesService {

    @Inject
    lateinit var api: StoriesApi

    init {
        DaggerApiComponent.create().inject(this)
    }

    /* Change this*/
    fun getStories(): Single<Story> {
        return api.getStories()
    }
}

和视图模型

.subscribeWith(object: DisposableSingleObserver<Story>() {
                    override fun onSuccess(value: Story?) {
                        //this a list of articles List that you won
                        stories.value = value.articles
                        storyLoadError.value = false
                        loading.value = false
                    }

                    override fun onError(e: Throwable?) {
                        storyLoadError.value = true
                        loading.value = false
                    }

                })

希望获得帮助


0
投票

步骤:1创建改造单例类

object RetrofitClient {
var loggingInterceptor = HttpLoggingInterceptor()
    .setLevel(HttpLoggingInterceptor.Level.BASIC)
var okHttpClient = OkHttpClient.Builder()
    .addInterceptor(loggingInterceptor)
    .build()
var service: ApiInterface

init {
    val retrofit = Retrofit.Builder().baseUrl(BASE_URL)
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create())
        .client(okHttpClient)
        .build()

    service = retrofit.create(ApiInterface::class.java)
  }
}

步骤:2 Api界面定义您的api

 interface ApiInterface {
  @GET("comments")
  fun getComments(
     @Query("id") postId: Int
   ): Observable<List<Comments>> (For rx java adapter)

   @GET("comments")
  fun getComments(
      @Query("id") postId: Int
   ): Call<List<Comments>>  (For retrofit call adapter)

  }

步骤:3存储库

class CommentsRepository {
private val TAG = AuthRepository::class.java.getSimpleName()
private var apiRequest = RetrofitClient.service
var data = MutableLiveData<List<Comments>>()


fun getPostComments(id: Int): LiveData<List<Comments>> {
     data = MutableLiveData()

     apiRequest.getComments(id).enqueue(object : Callback<List<Comments>> {
         override fun onResponse(
            call: Call<List<Comments>>,
             response: Response<List<Comments>>
         ) {
            if (response.isSuccessful) {
                data.value = response.body()
             }
        }

         override fun onFailure(call: Call<List<Comments>>, t: Throwable) {
             Log.e(TAG, "FFFeeeild:: " + t.message)
         }
     })
    return data
}

 fun getPostComments(id: Int): LiveData<List<Comments>> {  //For call object 
    data = MutableLiveData()
    apiRequest.getComments(id)
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(object : Observer<List<Comments>> {
            override fun onComplete() {
            }

            override fun onSubscribe(d: Disposable) {
             }
            override fun onNext(commentList: List<Comments>) {
                data.value = commentList
            }

            override fun onError(e: Throwable) {
                Log.e(TAG, e.toString())
            }
        })
    return data

  }
}

步骤:4 ViewModel

class CommentsViewModel(application: Application):AndroidViewModel(application){
private val repository: CommentsRepository = CommentsRepository()
private var postId: Int = -1
lateinit var userPost: LiveData<List<Comments>>

fun getPostComments(id: Int): LiveData<List<Comments>> {
    this.postId = id
    userPost = repository.getPostComments(id)
    return userPost
 }
}

步骤:5观察活动或破裂中的实时数据

class CommentsActivity : AppCompatActivity() {
 private lateinit var viewModel: CommentsViewModel
 viewModel = ViewModelProvider(this ).get(CommentsViewModel::class.java)

  viewModel.getPostComments(1).observe(this, object : Observer<List<Comments>> {
        override fun onChanged(commentsList: List<Comments>?) {
            commentsList?.forEach {
                it.apply {
                    var content = ""
                    content += "ID: $id\n"
                    content += "Post ID: $postId\n"
                    content += "Name: $name\n"
                    content += "Email: $email\n"
                            content += "Text: $text\n\n"
                    text_view_result.append(content)

                }

            }
        }

    })

  }

还请确保添加必要的库。

// Retrofit
def retrofitVersion = "2.5.0"
implementation "com.squareup.retrofit2:retrofit:$retrofitVersion"
implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"

// ViewModel and LiveData
def lifecycle_version = '2.2.0-alpha03'
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "com.squareup.okhttp3:okhttp:4.2.2"
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'



//Rx android library
implementation 'io.reactivex.rxjava2:rxjava:2.1.9'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'

希望它将帮助您让我知道是否还有任何疑问或遇到任何问题。

© www.soinside.com 2019 - 2024. All rights reserved.