Can you directly map ISO 8601 timestamps into OffsetDateTime or ZonedDateTime with Retrofit 2 and Gson?

2.8k views Asked by At

On Android, using Retrofit 2 and its Gson converter, you can map ISO 8601 strings like "2016-10-26T11:36:29.742+03:00" (in backend's JSON response) directly into java.util.Date fields in a POJO. This works neatly out of the box.

Now, I'm taking the ThreeTenABP lib into use (it provides backported versions of java.time classes), and I was wondering if it is possible to map the ISO timestamp strings directly into a nicer, more modern type, such as OffsetDateTime or ZonedDateTime.

In most circumstances (think Java 8 on server side), obviously, a conversion from "2016-10-26T11:36:29.742+03:00” into OffsetDateTime or ZonedDateTime would be straightforward, since the date string includes timezone information.

I tried using both OffsetDateTime and ZonedDateTime (instead of Date) in my POJOs, but at least out of the box it does not work. Any ideas if you can cleanly do that on Android with Retrofit 2?

Dependencies:

compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'

compile 'com.jakewharton.threetenabp:threetenabp:1.0.4'

Building the Retrofit instance:

new Retrofit.Builder()
// ...
.addConverterFactory(GsonConverterFactory.create())
.build();
2

There are 2 answers

0
laalto On BEST ANSWER

You can:

  1. Create a type adapter that implements JsonDeserializer<T> and converts the JSON literals to whatever ThreeTen types you want. Example for LocalDate:

    @Override
    public LocalDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        try {
            if (typeOfT == LocalDate.class) {
                return LocalDate.parse(json.getAsJsonPrimitive().getAsString(), DateTimeFormatter.ISO_DATE);
            }
        } catch (DateTimeParseException e) {
            throw new JsonParseException(e);
        }
        throw new IllegalArgumentException("unknown type: " + typeOfT);
    }
    

    Implementing similar for the ThreeTen types you want is left as an exercise.

  2. Register the type adapter on the GsonBuilder when building a Gson instance:

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(LocalDate.class, new YourTypeAdapter());
    Gson gson = gsonBuilder.create();
    
  3. Register the Gson instance with Retrofit.Builder:

    builder.addConverterFactory(GsonConverterFactory.create(gson));
    
  4. Use the ThreeTen types in your Gson model classes with Retrofit.

Similarly if you want to serialize ThreeTen types to JSON, also implement JsonSerializer in your type adapter.

1
leonardkraemer On

I created a small library that does exactly what laalto proposes in his answer, feel free to use it: Android Java Time Gson Deserializers