Retrofit GSON serialize Date from json string into java.util.date

84,438

Solution 1

Gson gson = new GsonBuilder()
    .setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
    .create();

RestAdapter restAdapter = new RestAdapter.Builder()
    .setEndpoint(API_BASE_URL)
    .setConverter(new GsonConverter.create(gson))
    .build();

Or the Kotlin equivalent:

val gson = GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create()
RestAdapter restAdapter = Retrofit.Builder()
    .baseUrl(API_BASE_URL)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()
    .create(T::class.java)

You can set your customized Gson parser to retrofit. More here: Retrofit Website

Look at Ondreju's response to see how to implement this in retrofit 2.

Solution 2

@gderaco's answer updated to retrofit 2.0:

Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss")
.create();

Retrofit retrofitAdapter = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();

Solution 3

Here is how I did it:

Create DateTime class extending Date and then write a custom deserializer:

public class DateTime extends java.util.Date {

    public DateTime(long readLong) {
        super(readLong);
    }

    public DateTime(Date date) {
        super(date.getTime());
    }       
}

Now for the deserializer part where we register both Date and DateTime converters:

public static Gson gsonWithDate(){
    final GsonBuilder builder = new GsonBuilder();

    builder.registerTypeAdapter(Date.class, new JsonDeserializer<Date>() {  

        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");  
        @Override  
        public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {  
            try {  
                return df.parse(json.getAsString());  
            } catch (final java.text.ParseException e) {  
                e.printStackTrace();  
                return null;  
            }  
        }
    });

    builder.registerTypeAdapter(DateTime.class, new JsonDeserializer<DateTime>() {  

        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
        @Override  
        public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {  
            try {  
                return new DateTime(df.parse(json.getAsString()));  
            } catch (final java.text.ParseException e) {
                e.printStackTrace();  
                return null;  
            }  
        }
    });

    return builder.create();
}

And when you create your RestAdapter, do the following:

new RestAdapter.Builder().setConverter(gsonWithDate());

Your Foo should look like this:

class Foo {
    Date date;
    DateTime created_at;
}

Solution 4

Gson can handle only one datetime format (those specified in builder) plus the iso8601 if parsing with custom format is not possible. So, a solution could be to write your custom deserializer. To solve your problem I defined:

package stackoverflow.questions.q18473011;

import java.util.Date;

public class Foo {

    Date date;
    Date created_at;

    public Foo(Date date, Date created_at){
       this.date = date;
       this.created_at = created_at;
    }

    @Override
    public String toString() {
       return "Foo [date=" + date + ", created_at=" + created_at + "]";
    }

}

with this deserializer:

package stackoverflow.questions.q18473011;

import java.lang.reflect.Type;
import java.text.*;
import java.util.Date;

import com.google.gson.*;

public class FooDeserializer implements JsonDeserializer<Foo> {

     public Foo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {

        String a = json.getAsJsonObject().get("date").getAsString();
        String b = json.getAsJsonObject().get("created_at").getAsString();

        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat sdfDateWithTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

        Date date, created;
        try {
           date = sdfDate.parse(a);
           created = sdfDateWithTime.parse(b);
        } catch (ParseException e) {
           throw new RuntimeException(e);
        }

        return new Foo(date, created);
    }

}

Final step is to create a Gson instance with right adapter:

package stackoverflow.questions.q18473011;

import com.google.gson.*;

public class Question {

    /**
     * @param args
     */
    public static void main(String[] args) {
      String s = "{ \"date\": \"2013-07-16\",    \"created_at\": \"2013-07-16T22:52:36Z\"}";


      GsonBuilder builder = new GsonBuilder();
      builder.registerTypeAdapter(Foo.class, new FooDeserializer());

      Gson gson = builder.create();
      Foo myObject = gson.fromJson(s, Foo.class);

      System.out.println("Result: "+myObject);
    }

}

My result:

Result: Foo [date=Tue Jul 16 00:00:00 CEST 2013, created_at=Tue Jul 16 22:52:36 CEST 2013]
Share:
84,438
jpotts18
Author by

jpotts18

Updated on July 23, 2020

Comments

  • jpotts18
    jpotts18 almost 4 years

    I am using the Retrofit library for my REST calls. Most of what I have done has been smooth as butter but for some reason I am having issues converting JSON timestamp strings into java.util.Date objects. The JSON that is coming in looks like this.

    {
        "date": "2013-07-16",
        "created_at": "2013-07-16T22:52:36Z",
    } 
    

    How can I tell Retrofit or Gson to convert these strings into java.util.Date objects?

  • Mohit Jain
    Mohit Jain almost 10 years
    Please explain your code in brief so that it is more useful for OP and other readers.
  • Juan Saravia
    Juan Saravia over 9 years
    Thanks! I only had to change from return df.parse(json.getAsString()); to long timeStamp = Long.parseLong(json.getAsString()); return new java.util.Date(timeStamp);
  • Zapnologica
    Zapnologica over 8 years
    How can I do this for all of my requests? So Add it to my ServiceGenerator?
  • Alireza Mirian
    Alireza Mirian over 8 years
    This actually doesn't apply locale for me. 2016-02-11T13:42:14.401Z gets deserialized to a date object which is for 13:42:14 in my locale.
  • Zapnologica
    Zapnologica over 7 years
    I have tried this, Downloaded items seems to be working now. But when I post a Json Object to the server. Will it then remove my time zone?
  • LukaszTaraszka
    LukaszTaraszka about 7 years
    I spent so much time for detecting problem with crashing my app with no reason after implementing this solution. When I copied and paste dete format something changed a quotation mark from 'T' to ’T’ - there is a different - watch carefully!
  • LukaszTaraszka
    LukaszTaraszka about 7 years
    Watch for quotation mark when copying date format - I had problem with that!
  • NickUnuchek
    NickUnuchek almost 7 years
    Great! Thank you!
  • Farid
    Farid almost 6 years
    What if I have 15-20 fields inside Foo (including custom objects), I have to deserialize them manually, right. That kills the purpose of Gson then.
  • Farid
    Farid almost 6 years
    You should have tried it with Retrofit first before "And you're done. no complicated overriding needed."
  • maddy23285
    maddy23285 over 4 years
    Find this after hours of searching and worked like a charm. Thanks
  • Bruno
    Bruno over 3 years
    Why have you not included the 'Z' parameter for timezone in the end of the string that goes on 'setDateFormat'? I wasted 2 hours here to discover that i should use '"yyyy-MM-dd'T'HH:mm:ss'Z'" instead.