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.

  • ¿Cómo no emitir datos de Flowable para testings unitarias?
  • Reemplazar el doble para cada uno por observable
  • Llamada asincrónica para cada elemento dentro de una colección
  • ¿Existe una regla de protección válida para RxJava y FasterXML?
  • Operador RxJava para el método de conmutación
  • Cambios en el valor de ObservableField no propagados
  • ¿Pasar lambdas a Observable.subscribe en kotlin dará como resultado pérdidas de memory?
  • EXCEPCIÓN FATAL: RxCachedThreadScheduler-1 cuando el gatillo se deshace. ¿Por qué?
  • Causado por: rx.exceptions.MissingBackpressureException
  • Problemas al analizar datos con RxJava + Kotlin
  • RxJava cómo crear Observable a partir de una Suscripción