0

I have an Android application written in Kotlin, that gets data from an API, for now it's just a local hosted JSON file. When I'm trying to get the data, I receive the error that my list, persons, is not initialized thus persons == null and didn't receive the data. I'm not sure what I did wrong and how to fix this.

The model

data class Person (
  @Json(name = "personId")
  val personId: Int,

  @Json(name = "personName")
  val name: String,

  @Json(name = "personAge")
  val age: Int,

  @Json(name = "isFemale")
  val isFemale: Boolean,
)

The JSON response

{
  "persons": [{
      "personId": 1,
      "personName": "Bert",
      "personAge": 19,
      "isFemale": "false",
    }
  ]
}

The ApiClient

class ApiClient {
    companion object {

        private const val BASE_URL = "http://10.0.2.2:3000/"

        fun getClient(): Retrofit {
            val moshi = Moshi.Builder()
                .add(customDateAdapter)
                .build()

            return Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(MoshiConverterFactory.create(moshi))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()
        }
    }
}

The Retrofit http methods

interface ApiInterface {

    @GET("persons")
    fun getPersons(): Observable<List<Person>>
}

and finally the call

class PersonActivity: AppCompatActivity(){
  private lateinit var jsonAPI: ApiInterface
  private lateinit var persons: List<Person>

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_person)

    val retrofit = ApiClient.getClient()
    jsonAPI = retrofit.create(ApiInterface::class.java)
    jsonAPI.getPersons()
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.computation())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ persons = it })
  }
}

Expected: Data from the JSON file into the persons list instead of NULL.

Walt
  • 111
  • 1
  • 2
  • 12

2 Answers2

0

Your moshi adapter is expecting the person objects directly but they are nested inside the "persons" Json array.

You should add another model as follow

data class Persons (
    @Json(name="persons")
    val persons : List<Person>
)

Also change the interface to return the new object.

interface ApiInterface {
    @GET("persons")
    fun getPersons(): Observable<Persons>
}

Will also need to change the subscription to

.subscribe({ persons = it.persons })
Giorgos Neokleous
  • 1,709
  • 1
  • 14
  • 25
  • While it makes sense what you're saying, it's still not working. But I can't see a request on my Rest server either, I'm hosting on http://localhost:3000/ and was told to use http://10.0.2.2:3000/ instead so the emulator can access it. – Walt May 02 '19 at 15:45
  • That's another issue though right? If you cannot access your API, the problem is not on the parsing. Check https://stackoverflow.com/a/33256827/3330058 to enable logging on Retrofit to check the errors – Giorgos Neokleous May 02 '19 at 15:47
  • I think so, I'm unsure but I can't see any requests made so I think they never get through? I can access the API perfectly fine through my web browser though. – Walt May 02 '19 at 15:51
  • Alright, I do see the requests coming through now, I forgot to add usesCleartextTraffic to the AndriodManifest file. But I still have the error, persons is not initialized – Walt May 02 '19 at 16:10
  • Yes, that's exactly what I have done, and still receive the error that persons is not initialized, and when debugging I can see it's persons is empty – Walt May 02 '19 at 16:22
0

I think there could be one of two issues.

If you are using moshi reflection you will have something like these dependencies in gradle:

//Moshi Core
implementation "com.squareup.moshi:moshi:1.8.0"

//Moshi Reflection
implementation "com.squareup.moshi:moshi-kotlin:1.8.0"

In that case you will need to use the KotlinJsonAdapterFactory:

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

If you are using codegen you'll need this:

apply plugin: 'kotlin-kapt'
...
//Moshi Core Artifact
implementation "com.squareup.moshi:moshi:1.8.0"

//Moshi Codegen
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.8.0"

Additionally, every class you want to deserialise to should be annotated with @JsonClass(generateAdapter = true) (so the Person and Persons class)

alexy
  • 464
  • 5
  • 6