Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Moshi 1.5 ignores @Json annotation on Kotlin class properties #315

Closed
consp1racy opened this issue May 26, 2017 · 37 comments
Closed

Moshi 1.5 ignores @Json annotation on Kotlin class properties #315

consp1racy opened this issue May 26, 2017 · 37 comments

Comments

@consp1racy
Copy link

@Keep
class RestUserSubscriptionPackage(
        val id: Int,
        @Json(name = "package_id") val package_prototype_id: Int,
        @Json(name = "package") val package_prototype: RestSubscriptionPackage?,
        val job_credit: JobCredit,
        val starts_at: OffsetDateTime,
        val ends_at: OffsetDateTime
)

package_prototype_id is always 0, package_prototype is always null.

Works out-of-the-box in Moshi 1.4.

Workaround: @field:Json

@swankjesse
Copy link
Collaborator

Try adding moshi-kotlin as a dependency and including it when you configure your Moshi instance.

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

@consp1racy
Copy link
Author

Not an option at the time, it crashes at runtime with Reflection on built-in Kotlin types is not yet fully supported. No metadata found for [compareTo(other: something): Int]. When I pinpoint what's happening, I'll open another issue.

@swankjesse
Copy link
Collaborator

Please do. Yeah, we wanna make sure this stuff all works well.

@ligi
Copy link

ligi commented Jun 27, 2017

Dam - moshi-kotlin is not yet an option on Android as for the size of the kotlin-reflect dependency - makes me need to go back to 1.4.0 for this reason.

@consp1racy
Copy link
Author

@ligi Have you tested after proguard?

@ligi
Copy link

ligi commented Jun 27, 2017

@consp1racy I did not really test - was reading https://medium.com/square-corner-blog/kotlins-a-great-language-for-json-fcd6ef99256b

If you’re using JSON, Moshi and Kotlin help you to build better models with less code. Note that moshi-kotlin uses kotlin-reflect for property binding. That dependency is large by Android standards (1.7 MiB / 11,500 methods). We’re thinking of creative ways to address that!

ligi referenced this issue in johnjohndoe/c3media-base Jul 2, 2017
@jaredsburrows
Copy link

jaredsburrows commented Jul 2, 2017

@consp1racy Thanks for posting this. I didn't want to roll back to 1.4.0. I have tested 1.4.0 and it does work out the box. Thanks for the @field:Json work around.

2 current solutions:

  • roll back to 1.4.0
  • use @field:Json

I completely agree with @ligi, the moshi-kotlin dependency is too large. We should not have to add another dependency that includes transitive dependencies in order to solve this issue. See the dependency count here: http://www.methodscount.com/?lib=com.squareup.moshi%3Amoshi-kotlin%3A1.5.0.

  • com.squareup.moshi:moshi-kotlin:1.5.0 - 23043 (total)
    • org.jetbrains.kotlin:kotlin-reflect:1.1.1 - 15095 (big one)

@swankjesse, though we may not retain most of these methods post proguard, it will most likely force many development builds to use multidex, which we do not want.

@JakeWharton
Copy link
Collaborator

JakeWharton commented Jul 2, 2017 via email

@dsdebastiani
Copy link

Same problem here even with moshi-kotlin dependencies.

Dependencies:

implementation "com.squareup.moshi:moshi-kotlin:1.5.0"
implementation "com.squareup.moshi:moshi-adapters:1.5.0"

Moshi config:

val moshi = Moshi.Builder()
                .add(KotlinJsonAdapterFactory())
                .add(RealmListJsonAdapterFactory())
                .add(Date::class.java, Rfc3339DateJsonAdapter())
                .build()

And the attributes:

 @Json(name = "picture_url")
    var pictureUrl: String? = null

@NightlyNexus
Copy link
Contributor

@dsdebastiani Can you make a minimum reproducible test case to show what's wrong?

@recoverrelax
Copy link

recoverrelax commented Oct 10, 2017

data class LoginSessionAuthDto(
@JSON(name = "access_token")
val accessToken: String
)

For me this works in debug mode, but in release it says accessToken can't be null. that class isn't being obfuscated, and i have the proper proguard rules (as stated in the main page). Only with @field:Json it works

@dsdebastiani
Copy link

Some solution for this?
Only works with @field:Json annotation.

@field:Json(name = "delivery_time")
var deliveryTime: Int? = null

@NightlyNexus
Copy link
Contributor

@dsdebastiani Are you using the KotlinJsonAdapterFactory? If so, can you provide a test case?

The following works:

@Test fun foo() {
  class Foo {
    @Json(name = "delivery_time")
    var deliveryTime: Int? = null
  }
  val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
  val adapter = moshi.adapter(Foo::class.java)
  assertThat(adapter.fromJson("{\"delivery_time\":60}")!!.deliveryTime).isEqualTo(60)
  assertThat(adapter.fromJson("{}")!!.deliveryTime).isNull()
  assertThat(adapter.toJson(Foo())).isEqualTo("{}")
  assertThat(adapter.toJson(Foo().apply { deliveryTime = 60 })).isEqualTo("{\"delivery_time\":60}")
}

@GediminasZukas
Copy link

GediminasZukas commented Nov 13, 2017

I have the same problem when trying to convert API field (in snake case) to entity class field (in camel case). Some example cases:

@Json(name = "url_z") val urlImage640: String? - doesn't work (urlImage640 is null)
@field:Json(name = "url_z") val urlImage640: String? - doesn't work (urlImage640 is null)
@Json(name = "url_z") val urlZ: String? - doesn't work (urlZ is null)
@Json(name = "url_z") val url_z: String? - Works
@Json(name = "owner") val ownerId: String - Works

Project info:

  • no proguard usage
  • android gradle plugin version 3.0
  • kotlin version 1.1.51
  • dependencies:
implementation 'com.squareup.retrofit2:converter-moshi:2.3.0'
implementation 'com.squareup.moshi:moshi-kotlin:1.5.0'

Retrofit & Moshi configuration in Dagger Di module:

    @Singleton
    @Provides
    fun provideJsonConverter(): Moshi {
        return Moshi.Builder()
                .add(KotlinJsonAdapterFactory())
                .build()
    }

    @Singleton
    @Provides
    fun provideRestClient(moshi: Moshi): Retrofit {
        return Retrofit.Builder()
                .baseUrl(BuildConfig.FLICKR_ENDPOINT)
                .addConverterFactory(MoshiConverterFactory.create(moshi))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()
    }

Json example:

{
  "photos": {   
    "photo": [
      {
        "id": "1111",
        "owner": "11111DD",
        "title": "sss",
        "ownername": "bob",
        "iconserver": "0001",
        "iconfarm": 1,
        "url_z": "urlz",
        "url_n": "urln",  
        "url_m": "urlm"
      }
    ]
  }
}  

Entity classes for this Json parsing:

data class PhotosResponse constructor(@Json(name = "photos") val photosEntity: PhotosEntity)

data class PhotosEntity constructor(@Json(name = "photo") val photoList: List<PhotoEntity>)

data class PhotoEntity constructor(@Json(name = "id") val id: Long,
                              @Json(name = "title") val title: String,
                              @Json(name = "url_m") val urlImage240: String?,
                              @Json(name = "url_n") val urlImage320: String?,
                              @Json(name = "url_z") val urlImage640: String?,
                              @Json(name = "ownername") val ownerName: String,
                              @Json(name = "owner") val ownerId: String,
                              @Json(name = "iconserver") val iconServer: Long,
                              @Json(name = "iconfarm") val iconFarm: Long)

Is it a bug or I am doing something wrong?

@NightlyNexus
Copy link
Contributor

@GediminasZukas

@Test fun f() {
  val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
  val adapter = moshi.adapter(PhotosResponse::class.java)
  val response = adapter.fromJson("""
    {
      "photos": {
        "photo": [
          {
            "id": "1111",
            "owner": "11111DD",
            "title": "sss",
            "ownername": "bob",
            "iconserver": "0001",
            "iconfarm": 1,
            "url_z": "urlz",
            "url_n": "urln",
            "url_m": "urlm"
          }
        ]
      }
    }
  """.trimIndent())
  assertThat(response).isEqualTo(PhotosResponse(PhotosEntity(listOf(PhotoEntity(
      1111, "sss", "urlm", "urln", "urlz",
      "bob","11111DD", 1, 1)))))
}

passes.

What specifically doesn't work (thrown exception, unexpected result)? Can you form it in a test case?

@GediminasZukas
Copy link

GediminasZukas commented Nov 13, 2017

@NightlyNexus , your test case clarifies everything. It was my side issue, thank you.

@premnirmal
Copy link

premnirmal commented Feb 26, 2018

@GediminasZukas what was the fix for the issue on your side? I believe I'm facing the same issue.

@png6
Copy link

png6 commented Feb 26, 2018

@premnirmal bug in the KotlisJsonAdapterFactory? I noticed the @field:Json issue as well when proguard is used.

@jaredsburrows
Copy link

I am not sure why this is closed. In Moshi 1.4 we did not need to do the @field.

@swankjesse
Copy link
Collaborator

Moshi 1.4 didn’t support Kotlin. It sort of worked because Moshi’s did Java reflection on the objects. But it was fragile, especially around nulls.

@jaredsburrows
Copy link

@swankjesse Ok. Thanks for the explanation. Adding @field: is no big deal.

@NightlyNexus
Copy link
Contributor

In Moshi 1.4 we did not need to do the @field.

right, cuz @Target for fields was removed from @Json. but, yeah, Kotlin was not officially supported.

bug in the KotlinJsonAdapterFactory? I noticed the @field:Json issue as well when proguard is used.

sounds like a ProGuard issue, not a bug here. adding the KotlinJsonAdapterFactory makes @Json work as expected.

@png6
Copy link

png6 commented Feb 26, 2018

@NightlyNexus so is this a matter of setting the correct rules?

@NightlyNexus
Copy link
Contributor

@png6 Unsure. I don't use ProGuard, personally. Somebody on Stack Overflow or a ProGuard forum might know?

@flux87
Copy link

flux87 commented Mar 9, 2018

Well, had the same issue ...

// Not working
@Json(name = "first_name")
val firstName: String = "" // VAL
// Working
@Json(name = "first_name")
var firstName: String = "" // VAR

Maybe because it was read-only ...

@amitav13
Copy link

amitav13 commented Jun 9, 2018

Is using @field:Json now the recommended way if we don't want to use moshi-kotlin?

@swankjesse
Copy link
Collaborator

Use moshi-kotlin. Nothing else is tested.

@nachtien
Copy link

@swankjesse, but that has the Kotlin reflect library and is huge right? Also a bit slow to initialize especially on older devices? We opted out of using it for these reasons.

@swankjesse
Copy link
Collaborator

Then use the Kotlin codegen from the most recent release?

@nachtien
Copy link

Does that fix this issue? We were going to bring in codgen for just the null support.

@swankjesse
Copy link
Collaborator

Yes, Kotlin support is the solution to mistargetted @Json annotations.

@nachtien
Copy link

nachtien commented Jun 24, 2018 via email

@abbasiehsan1991
Copy link

I just faced the same problem, I had some variables with underline like row_type and its value was null all the time. I fixed this problem by setting this annotation for my data class:
@JsonClass(generateAdapter = true)

hossain-khan added a commit to hossain-khan/android-hk-vision-muzei-plugin that referenced this issue May 10, 2020
@desgraci
Copy link

@flux87 I had to add a var and use a dummy value to my fields.

using:
implementation "com.squareup.moshi:moshi-kotlin:1.9.2"

@jewom
Copy link

jewom commented Nov 15, 2020

Not working :

@Json(name = "image_url")
    val imageUrl: String

Working :

@field:Json(name = "image_url")
    val imageUrl: String

Do I have to use the field:Json anotation ?

@yatoooon
Copy link

same problem

@ZacSweers
Copy link
Collaborator

Locking this as it's no longer constructive. Please use discussions to ask usage questions, and if you have a bug to report please do so with a minimally reproducible sample.

@square square locked as resolved and limited conversation to collaborators May 26, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests