Retrofit GSON serialize Date from json string into long or java.lang.Long
Asked Answered
E

7

9

I am using the Retrofit library for my REST calls. The JSON that is coming in looks like this.

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

How can I tell Retrofit or Gson to convert this into long?

Ensheathe answered 16/2, 2015 at 11:17 Comment(3)
you can use a custom type converter (but I don't know how to simply restrict one of these to a specific field instead of a whole type)Spearhead
as a side note, this Z looks strange. I would bet on a formatting issue on the server side.Spearhead
the Z simple means there is no offset from UTC - more info from wikipediaHilburn
C
26

You can easily do this by setting a custom GsonConverter with your own Gson object on the retrofit instance. In your POJO you can Date created_at; instead of a long or a String. From the date object you can use created_at.getTime() to get the long when necessary.

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

RestAdapter.Builder builder = new RestAdapter.Builder();
// Use a custom GSON converter
builder.setConverter(new GsonConverter(gson));
..... create retrofit service.

You can also support multiple Date string formats by registering a custom JsonDeserializer on the gson instance used by retrofit

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Date.class, new DateTypeDeserializer());

public class DateTypeDeserializer implements JsonDeserializer<Date> {
    private static final String[] DATE_FORMATS = new String[]{
            "yyyy-MM-dd'T'HH:mm:ssZ",
            "yyyy-MM-dd'T'HH:mm:ss",
            "yyyy-MM-dd",
            "EEE MMM dd HH:mm:ss z yyyy",
            "HH:mm:ss",
            "MM/dd/yyyy HH:mm:ss aaa",
            "yyyy-MM-dd'T'HH:mm:ss.SSSSSS",
            "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS",
            "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS'Z'",
            "MMM d',' yyyy H:mm:ss a"
    };

    @Override
    public Date deserialize(JsonElement jsonElement, Type typeOF, JsonDeserializationContext context) throws JsonParseException {
        for (String format : DATE_FORMATS) {
            try {
                return new SimpleDateFormat(format, Locale.US).parse(jsonElement.getAsString());
            } catch (ParseException e) {
            }
        }
        throw new JsonParseException("Unparseable date: \"" + jsonElement.getAsString()
                + "\". Supported formats: \n" + Arrays.toString(DATE_FORMATS));
    }
}
Consuetudinary answered 16/2, 2015 at 20:41 Comment(0)
C
5

Read it in as a string in your POJO then use your getter to return it as a long:

String created_at;

public long getCreatedAt(){
    SimpleDateFormat formatter = new 
              SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    Date createDate = formatter.parse(created_at);

    return createDate.getTime();
 }

the SimpleDateFormat string can be referenced here

Cardoza answered 16/2, 2015 at 18:44 Comment(1)
@Lion789 without seeing your output or really being aware the differences of DateFormat vs SimpleDateFormat I'd say try using SimpleDateFormat.Cardoza
K
3

You could simply use this setDateFormat() method to do it

public class RestClient
{
    private static final String BASE_URL = "your base url";
    private ApiService apiService;

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

        RestAdapter restAdapter = new RestAdapter.Builder()
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .setEndpoint(BASE_URL)
                .setConverter(new GsonConverter(gson))
                .build();

        apiService = restAdapter.create(ApiService.class);
    }

    public ApiService getApiService()
    {
        return apiService;
    }
}
Kelt answered 19/2, 2015 at 10:25 Comment(1)
You're sure the quotes are the best idea here, that doesn't even make any sense. Yet, it throws IllegalArgumentException: Unterminated quote when initialized. You could have seen if you had tested it before postingMaggee
E
2

After reading the docs, I tried this. It works but i dont know if it will have any effect on other types.

I needed to have a long in my POJO, coz i dont wanna convert when saving into db.

I used a custom deserializer

JsonDeserializer<Long> deserializer = new JsonDeserializer<Long>() {
    @Override
    public Long deserialize(JsonElement json, Type typeOfT,
        JsonDeserializationContext context) throws JsonParseException {
        try{
            if(json==null){
               return new Long(0);
            }
            else{
                String dateString = json.getAsString();
                long dateLong = DateFormatUtil.getLongServerTime(dateString);
                return new Long(dateLong);
            }
        }
        catch(ParseException e){
            return new Long(0);
        }
    }
};

and using it

Gson gson = new GsonBuilder()
    .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
    .setDateFormat(patternFromServer)
    .registerTypeAdapter(Long.class, deserializer)
    .create();
Ensheathe answered 17/2, 2015 at 1:5 Comment(1)
Do you need specify .setDateFormat(...)? Mine worked without that.Portable
A
2

Below code is tested and finally working after lot efforts. before creating retrofit object create Gson object

 Gson gson = new GsonBuilder()
                .registerTypeAdapter(Date.class, new DateDeserializer())
                .registerTypeAdapter(Date.class, new DateSerializer())
                .create();

and now create retrofit instance

 Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("URL")
                    .addConverterFactory(GsonConverterFactory.create(gson))
                    .build();

create these two classes to serialize and deserialize date format

static class DateDeserializer implements JsonDeserializer<Date> {

        private final String TAG = DateDeserializer.class.getSimpleName();

        @Override
        public Date deserialize(JsonElement element, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
            String date = element.getAsString();

            SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
            formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
            Date returnDate = null;
            try {
                returnDate = formatter.parse(date);
            } catch (ParseException e) {
                Log.e(TAG, "Date parser exception:", e);
                returnDate = null;
            }
            return returnDate;
        }
    }

    static class DateSerializer implements JsonSerializer<Date> {

        private final String TAG = DateSerializer.class.getSimpleName();

        @Override
        public JsonElement serialize(Date date, Type type, JsonSerializationContext jsonSerializationContext) {
            SimpleDateFormat formatter = new SimpleDateFormat(DATE_FORMAT);
            formatter.setTimeZone(TimeZone.getDefault());
            String dateFormatAsString = formatter.format(date);
            return new JsonPrimitive(dateFormatAsString);
        }

    }
Ashraf answered 20/10, 2016 at 14:39 Comment(1)
Oh yeah, tried very same thing. Somehow, works when on test class (direct Gson conversion). But fails when added to Retrofit. Actually, when used with Retrofit Ovveriden deserialize won't even get calledMaggee
B
0

You can set the parse pattern in the GsonBuilder.setDateFormat() and set Date object in the POJO and then just call Date.getMillis()

setDateFormat()

Boehmenist answered 16/2, 2015 at 19:8 Comment(0)
H
0

You should be using a type adapter in your Gson instance through

new GsonBuilder()
    .registerTypeAdapter(Date.class, [date deserializer class here])
    .create

But can I suggest that instead of using a SimpleDateFormatter you take a look at this implementation from FasterXML/jackson-databind implementations for ISO8601 date handling in this class and this one. It very much depends on whether you are parsing a lot of dates or not.

Also note that we ran into issues with using SimpleDateFormatter inside an Android application (we use a combination of Java and Android libraries) where there were different results between parsing in Java and Android. Using the above implementations helped to solve this issue.

This is the Java implementation and this is the Android implementation, not the definition for Z and X in the Java implementation and the missing X implementation in Android

Hilburn answered 19/2, 2015 at 9:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.