Adaptador personalizado de Moshi con RxAndroid & Retrofit & Kotlin

Después de configurar Kotlin para el proyecto de Android, escribí un sencillo MainActivity.kt . Llamó a Retrofit para get un file JSON que contenía los siguientes datos:

 {
     "post": "éxito",
     "usuario": {
         "nombre de usuario": "Eric"
     }
 }

Ahora quiero usar Moshi para convertir los datos JSON a la class de Kotlin, así que aquí están las dos classs para reflejar la estructura JSON anterior:

 Usuario de class (nombre de usuario var: String)

 class UserJson (var message: String, var user: User)

Y un adaptador de tipo personalizado para Moshi:

 class UserAdapter {
     @FromJson divertido deJson (usuarioJson: UserJson): Usuario {
         Log.d ("MyLog", "message = $ {userJson.message}") // = éxito
         Log.d ("MyLog", "usuario = $ {usuarioJson.usuario}") // = nulo

         return userJson.user
     }
 }

Cuando entra en la function de fromJson() , userJson.message = "success" como se esperaba. Pero lo extraño es que userJson.user es null , que debería ser User(username="Eric") .

Soy nuevo en Moshi y Kotlin, y ya me he quedado con este problema durante aproximadamente 10 horas. Por favor, ayúdame. Gracias por cualquier ayuda.

========================================

El siguiente es el código completo de MainActivity.kt (solo 50 líneas):

 class MainActivity: AppCompatActivity () {

     anular diversión onCreate (savedInstanceState: Bundle?) {
         super.onCreate (savedInstanceState)
         setContentView (R.layout.activity_main)

         // Adaptadores de tipo personalizado para Moshi
         val userMoshi = Moshi.Builder (). add (UserAdapter ()). build ()

         val retrofit = Retrofit.Builder ()
                 .baseUrl ("https://dl.dropboxusercontent.com/")
                 .addConverterFactory (MoshiConverterFactory.create (userMoshi))
                 .addCallAdapterFactory (RxJava2CallAdapterFactory.create ())
                 .build()

         val accountService = retrofit.create (AccountService :: class.java)

         accountService.signUpAnonymously ()
                 .subscribeOn (Schedulers.io ())
                 .observeOn (AndroidSchedulers.mainThread ())
                 .subscribe {user ->
                     Log.d ("MyLog", user.toString ())
                 }
     }
 }


 // ========== Para Retrofit ==========
 interfaz AccountService {

     @GET ("u / 17350105 / test.json")
     diversión signUpAnonymously (): Observable <Usuario>

 }


 // ========== Para Moshi ==========
 Usuario de class (nombre de usuario var: String)

 class UserJson (var message: String, var user: User)

 class UserAdapter {

     @FromJson divertido deJson (usuarioJson: UserJson): Usuario {
         Log.d ("MyLog", "message = $ {userJson.message}") // = éxito
         Log.d ("MyLog", "usuario = $ {usuarioJson.usuario}") // = nulo

         return userJson.user
     }

 }

El build.gradle es:

 compile "io.reactivex.rxjava2: rxjava: 2.0.0"
 compile "io.reactivex.rxjava2: rxandroid: 2.0.0"

 comstackr "com.android.support:appcompat-v7:25.0.0"

 compile "com.squareup.retrofit2: retrofit: 2.1.0"
 comstack "com.squareup.retrofit2: converter-moshi: 2.1.0"
 comstackr 'com.jakewharton.retrofit: retrofit2-rxjava2-adapter: 1.0.0'

Gracias de nuevo.

Puede resolver el problema cambiando su código para hacer algo como a continuación.

Básicamente en su caso cuando el UserAdapter está registrado, le dice a moshi que puede crear un User solo desde el object UserJson . Por lo tanto, Moshi no reconoce el object JSON con el user palabra key.

Al agregar una indirección en forma de User1 (perdón por la convención de nomenclatura), UserJson se crea correctamente con User1 de JSON.

 class User(var username: String) class User1(var username: String) // I introduced this class class UserJson(var message: String, var user: User1) // changed User to User1 class UserAdapter { @FromJson fun fromJson(userJson: UserJson): User { println("message = ${userJson.message}") println("user = ${userJson.user}") return User(userJson.user.username) } } 

Si solo necesitas el object User . Hay una biblioteca llamada Moshi-Lazy-Adapters que proporciona una anotación @Wrapped , que permite especificar la ruta al object deseado. Todo lo que tiene que hacer es agregar el adaptador correspondiente a su instancia de moshi y cambiar el código de service a:

 interface AccountService { @GET("u/17350105/test.json") @Wrapped("user") fun signUpAnonymously() : Observable<User> } 

No es necesario ningún otro adaptador personalizado.

  • Kotlin: Cómo convertir la testing que usa Thread.sleep a RxJava TestScheduler
  • Aplicando transformación a cada elemento en Single <List <T >>
  • Cómo get el valor del ObservableField en android
  • Kotlin y RxJava2 zip operator - Ninguna de las siguientes funciones puede invocarse con los arguments suministrados
  • RxJava2 cómo separar diferentes implementaciones de emisor observable
  • Espere hasta que dos observables emitan verdadero
  • Cómo get el valor emitido desde el primer observable
  • Reaccionar a la llamada de actualización
  • Cómo burlarse del repository reactivo que devuelve Observable
  • Cómo consultar Realm en el background Subprocess usando RxJava2
  • Comportamiento impar de TestObserver al suscribirse a un Asunto