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

Kotlin Duration Adapter fails to be generated with codegen #1796

Open
andretmarques opened this issue Jan 27, 2024 · 0 comments
Open

Kotlin Duration Adapter fails to be generated with codegen #1796

andretmarques opened this issue Jan 27, 2024 · 0 comments
Labels

Comments

@andretmarques
Copy link

When integrating custom adapters and annotations with kotlin.time.Duration, the expected behavior is for the adapter to accurately convert JSON's long values into Kotlin's Duration type. However, I've observed that the deserialization process doesn't work as expected. Instead of obtaining a Duration object, the output remains as a Long type. This issue seems to occur during the deserialization process, indicating a potential problem in the handling of Kotlin's Duration type.

Here is the adapter and annotations created:

@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class DurationMillis

@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class DurationSeconds

@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class DurationMinutes

object DurationJsonAdapter {
  @ToJson
  fun millisToJson(@DurationMillis duration: Duration): Long = duration.inWholeMilliseconds

  @FromJson
  @DurationMillis
  fun millisFromJson(value: Long): Duration = value.milliseconds

  @ToJson
  fun secondsToJson(@DurationSeconds duration: Duration): Long = duration.inWholeSeconds

  @FromJson
  @DurationSeconds
  fun secondsFromJson(value: Long): Duration = value.seconds

  @ToJson
  fun minutesToJson(@DurationMinutes duration: Duration): Long = duration.inWholeMinutes

  @FromJson
  @DurationMinutes
  fun minutesFromJson(value: Long): Duration = value.minutes
}

with some tests included:

class DurationJsonAdapterTest {
  @JsonClass(generateAdapter = true)
  data class DurationTypes(
    @Json(name = "durationMillis") @DurationMillis val durationMillis: Duration,
    @Json(name = "durationSeconds") @DurationSeconds val durationSeconds: Duration,
    @Json(name = "durationMinutes") @DurationMinutes val durationMinutes: Duration,
  )

  private val moshi = Moshi.Builder()
    .add(DurationJsonAdapter)
    .build()


  @OptIn(ExperimentalStdlibApi::class)
  @Test
  fun fromJson() {
    val adapter = moshi.adapter<DurationTypes>()
    val json = """{"durationMillis": 120000, "durationSeconds": 120, "durationMinutes": 2}"""
    val javanDuration = adapter.fromJson(json)
    assertEquals(2.minutes, javanDuration?.durationMillis)
    assertEquals(2.minutes, javanDuration?.durationSeconds)
    assertEquals(2.minutes, javanDuration?.durationMinutes)
  }

  @OptIn(ExperimentalStdlibApi::class)
  @Test
  fun toJson() {
    val adapter: JsonAdapter<DurationTypes> = moshi.adapter<DurationTypes>()
    val javanDuration = DurationTypes(2.minutes, 2.minutes, 2.minutes)
    val json = adapter.toJson(javanDuration)
    assertEquals("""{"durationMillis":120000, "durationSeconds":120, "durationMinutes":2}""", json)
  }
}

The output:
image

Inspecting the factories:
image

With Java Duration (java.time.Duration) works perfectly well
image
image

class JavaDurationJsonAdapterTest {
  @JsonClass(generateAdapter = true)
  data class DurationTypes(
    @Json(name = "durationMillis") @JavaDurationMillis val durationMillis: JavaDuration,
    @Json(name = "durationSeconds") @JavaDurationSeconds val durationSeconds: JavaDuration,
    @Json(name = "durationMinutes") @JavaDurationMinutes val durationMinutes: JavaDuration,
  )

  private val moshi = Moshi.Builder()
    .add(JavaDurationJsonAdapter)
    .build()


  @OptIn(ExperimentalStdlibApi::class)
  @Test
  fun fromJson() {
    val adapter = moshi.adapter<DurationTypes>()
    val json = """{"durationMillis": 120000, "durationSeconds": 120, "durationMinutes": 2}"""
    val javanDuration = adapter.fromJson(json)
    assertEquals(JavaDuration.ofMinutes(2), javanDuration?.durationMillis)
    assertEquals(JavaDuration.ofMinutes(2), javanDuration?.durationSeconds)
    assertEquals(JavaDuration.ofMinutes(2), javanDuration?.durationMinutes)
  }

  @OptIn(ExperimentalStdlibApi::class)
  @Test
  fun toJson() {
    val adapter: JsonAdapter<DurationTypes> = moshi.adapter<DurationTypes>()
    val javanDuration = DurationTypes(JavaDuration.ofMinutes(2), JavaDuration.ofMinutes(2), JavaDuration.ofMinutes(2))
    val json = adapter.toJson(javanDuration)
    assertEquals("""{"durationMillis":120000,"durationSeconds":120,"durationMinutes":2}""", json)
  }
}

@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class JavaDurationMillis

@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class JavaDurationSeconds

@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
annotation class JavaDurationMinutes

object JavaDurationJsonAdapter {
  @ToJson
  fun millisToJson(@JavaDurationMillis duration: JavaDuration): Long = duration.toMillis()

  @FromJson
  @JavaDurationMillis
  fun millisFromJson(value: Long): JavaDuration = JavaDuration.ofMillis(value)

  @ToJson
  fun secondsToJson(@JavaDurationSeconds duration: JavaDuration): Long = duration.toSeconds()

  @FromJson
  @JavaDurationSeconds
  fun secondsFromJson(value: Long): JavaDuration = JavaDuration.ofSeconds(value)

  @ToJson
  fun minutesToJson(@JavaDurationMinutes duration: JavaDuration): Long = duration.toMinutes()

  @FromJson
  @JavaDurationMinutes
  fun minutesFromJson(value: Long): JavaDuration = JavaDuration.ofMinutes(value)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant