Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Moshi LocalDateTime adapter with multiple format

By default, ThreeTenABP.LocalDateTime is converted to

{"date":{"day":10,"month":4,"year":2018},"time":{"hour":3,"minute":34,"nano":115000000,"second":18}}

I can write an adapter to support ISO date string 2018-04-10T03:45:26.009

class LocalDateTimeAdapter {
    @ToJson
    fun toJson(value: LocalDateTime): String {
        return FORMATTER.format(value)
    }

    @FromJson
    fun fromJson(value: String): LocalDateTime {
        return FORMATTER.parse(value, LocalDateTime.FROM)
    }

    companion object {
        private val FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME
    }
}

How can I write an adapter which can support both format (fromJson)

  • {"date":{"day":10,"month":4,"year":2018},"time":{"hour":3,"minute":34,"nano":115000000,"second":18}}
  • 2018-04-10T03:45:26.009

Beside identifying which the format is used in fromJson, I am curious how Moshi internally perform toJson/fromJson for LocalDateTime

like image 479
Desmond Lua Avatar asked Feb 04 '23 00:02

Desmond Lua


1 Answers

You’ll need to use JsonReader.peek() to determine the format of the incoming JSON, and then take action accordingly.

First install an adapter that converts LocalDateTime to a string. That adapter should use a qualifier annotation.

@Retention(RetentionPolicy.RUNTIME)
@JsonQualifier
@interface DateString {
}

Next create the string adapter. It should be straightforward, and might delegate to Moshi’s built-in Rfc3339DateJsonAdapter.

public final class LocalDateAsStringAdapter {
  @ToJson String toJson(@DateString LocalDateTime localDateTime) {
    ...
  }

  @FromJson @DateString LocalDateTime fromJson(String string) {
    ...
  }
}

Finally create an adapter that delegates either to Moshi’s built in adapter (that one will use {...}) or to your string adapter. This one prefers the string format, but you can do what you like.

public final class MultipleFormatsDateAdapter {
  @ToJson void toJson(JsonWriter writer, LocalDateTime value,
      @DateString JsonAdapter<LocalDateTime> stringAdapter) throws IOException {
    stringAdapter.toJson(writer, value);
  }

  @FromJson LocalDateTime fromJson(JsonReader reader, @DateString JsonAdapter<LocalDateTime> stringAdapter,
      JsonAdapter<LocalDateTime> defaultAdapter) throws IOException {
    if (reader.peek() == JsonReader.Token.STRING) {
      return stringAdapter.fromJson(reader);
    } else {
      return defaultAdapter.fromJson(reader);
    }
  }
}

This works because Moshi lets you declare multiple JsonAdapter arguments to the @ToJson and @FromJson methods, and these arguments may be annotated.

It also relies on the way this feature works if the types are the same. Here we’re making a JsonAdapter<LocalDateTime> by delegating to another JsonAdapter<LocalDateTime>. When the types are the same Moshi uses its nextAdapter() feature for composition.

like image 78
Jesse Wilson Avatar answered Feb 06 '23 14:02

Jesse Wilson