Using moshi and moshi-kotlin 1.5.
When using moshi kotlin, the kotlin data class constructor variables are to be marked with @field:Json for them to be recognized correctly by the reflection library on obfuscated builds. I noticed that in my unit tests, this doesn't work. In unit tests, the only way to get moshi to recognize the field and parse it is to annotate it as @Json (notice that the @field prefix isn't added).
This means that my models must have both the annotations @field:Json and @Json.
Example:
Moshi instance:
return new Moshi.Builder()
.add(new KotlinJsonAdapterFactory())
.build();
User class:
data class User(
@field:Json(name = "user")
var inner: UserInner? = null)
Parsed in app code (obfuscated):
User user = moshi.adapter(User.class).fromJson(jsonString);
Log.d("User", user.getInner().toString());
// user.getInner() is not null
Fails when parsed in unit tests:
@RunWith(JUnit4.class)
class UserTest extends junit.framework.TestCase {
@Test
public void testUser() {
User user = moshi.adapter(User.class).fromJson(jsonString);
assertNotNull(user.getInner()); // fails!
}
}
When I update the user class to include both annotations:
data class User(
@Json(name = "user") @field:Json(name = "user")
var inner: UserInner? = null)
Now the tests pass.
My unit tests are standard JUnit4 unit tests written in java:
Edit:
The json adapter doesn't find the annotation because the jsonAnnotation is null when accessed here in this line:
https://github.com/square/moshi/blob/master/kotlin/src/main/java/com/squareup/moshi/kotlin/KotlinJsonAdapter.kt#L223
However, when running a proguarded build on jdk 1.7 on an android device, the annotation is present.
Only in unit tests (jdk 1.8) the annotation is null if denoted with @field:Json.
Could this be related to the environment that runs the moshi adapter, particularly the VM?
Just use @Json? There should be no need to use @field:Json when using the KotlinJsonAdapter.
I'm surprised this works on JDK 1.7, though...
@NightlyNexus @Json doesn't work. See https://github.com/square/moshi/issues/315
Have a failing test case?
From your code above, this passes.
data class User(@Json(name = "user") val name: String)
@Test fun user() {
val user = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
.adapter(User::class.java)
.fromJson("""{"user":"jw"}""")
assertThat(user).isEqualTo(User("jw"))
}
@NightlyNexus it passes because you added the @Json annotation. The code you posted will fail on an android device when the build is obfuscated with proguard
@NightlyNexus if you use the @field:Json annotation and run the unit test, it will fail. But @field:Json is required if you want moshi-kotlin to work with proguard according to the discussion here: https://github.com/square/moshi/issues/315.
I suspect we need a really good ProGuard section on our README.
So what is the final solution ? I still have to use both @Json and @field:Json if I want it to work both in Debug and Release with obfuscation. I used the proguard rules given in the README but it still does not work.
@suchet-q that's right - I have to use both annotations for this to work properly.
I have found the solution, you can use the annotation on top of your data class @JsonClass(generateAdapter = true), which can be found in the moshi-kotlin-codegen depedence.
This way you just need the @Json annotation, and it works perfectly fine in release with proguard obfuscation.
Most helpful comment
I suspect we need a really good ProGuard section on our README.