Модифицировать GSON сериализовать дату из строки json в java.util.date

Я использую библиотеку Retrofit для своих вызовов REST. Большая часть того, что я сделал, была гладкой, как масло, но по какой-то причине у меня возникают проблемы с преобразованием строк меток времени JSON в java.util.Date объекты. Входящий JSON выглядит следующим образом.

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

Как я могу сказать Retrofit или Gson преобразовать эти строки в java.util.Date objects?

6 ответов

Решение
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();

Вы можете настроить свой собственный парсер Gson для модернизации. Подробнее здесь: Модифицированный сайт

Посмотрите на ответ Ондрею, чтобы увидеть, как реализовать это в модернизации 2

Ответ @gderaco обновлен до версии 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();

Вот как я это сделал:

Создайте класс DateTime с расширением Date, а затем напишите настраиваемый десериализатор:

public class DateTime extends java.util.Date {

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

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

Теперь для части десериализатора, где мы регистрируем конвертеры Date и DateTime:

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();
}

И когда вы создаете свой RestAdapter, сделайте следующее:

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

Ваш Foo должен выглядеть так:

class Foo {
    Date date;
    DateTime created_at;
}

Gson может обрабатывать только один формат даты и времени (указанный в компоновщике) плюс iso8601, если анализ с пользовательским форматом невозможен. Таким образом, решение может быть написать ваш собственный десериализатор. Чтобы решить вашу проблему, я определил:

package stackru.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 + "]";
    }

}

с этим десериализатором:

package stackru.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);
    }

}

Последний шаг - создать Gson экземпляр с правым адаптером:

package stackru.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);
    }

}

Мой результат:

Result: Foo [date=Tue Jul 16 00:00:00 CEST 2013, created_at=Tue Jul 16 22:52:36 CEST 2013]

В буквальном смысле, если у вас уже есть объект Date с именем "create_at" в классе, который вы создаете, тогда это легко:

Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").create();
YourObject parsedObject1 = gson.fromJson(JsonStringYouGotSomehow, YourObject.class);

И вы сделали. нет необходимости в сложном переопределении.

Вы можете определить два новых класса, как это:

import java.util.Date;

public class MyDate extends Date {
}

а также

import java.util.Date;

public class CreatedAtDate extends Date {
}

Ваш POJO будет таким:

import MyDate;
import CreatedAtDate;

public class Foo {
    MyDate date;
    CreatedAtDate created_at;
}

Наконец, установите свой десериализатор:

public class MyDateDeserializer implements JsonDeserializer<Date> {

    public static final SimpleDateFormat sServerDateDateFormat = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public MyDate deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
         if (json != null) {
            final String jsonString = json.getAsString();
            try {
                return (MyDate) sServerDateDateFormat.parse(jsonString);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}

а также

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(MyDate.class, new MyDateDeserializer());

Это не дает прямого ответа на заданный вопрос, но, на мой взгляд, это "современное состояние", если кодировщик имеет полную свободу выбора того, как решить проблему.

Во-первых, использование java.util.Date - не лучшее решение. Причина в том, что эти классы не имели идеального поведения в некоторых угловых случаях, поэтому, если они были заменены классом Java Instant и т. Д., Проверьте ответ Базиля Бурка в этом вопросе SO: Создание объектов Date в Kotlin для уровня API меньше или равного 16

Итак, я использовал Instant класс ThreeTenABP и Kotlin на Android:

val gson = GsonBuilder().registerTypeAdapter(Instant::class.java,
    JsonDeserializer<Instant> { json: JsonElement, _: Type?, _: JsonDeserializationContext? ->
        ZonedDateTime.parse(
            json.asJsonPrimitive.asString
        ).toInstant()
    }
).create()

val retrofit = Retrofit.Builder()
    .baseUrl(baseUrl)
    .addConverterFactory(GsonConverterFactory.create(gson))
    .build()
Другие вопросы по тегам