Доопрацювати дату серіалізації GSON із рядка json у java.util.date


83

Я використовую бібліотеку модернізації для своїх дзвінків REST. Більшість з того, що я зробив, було гладким, як масло, але чомусь у мене виникають проблеми з перетворенням рядків міток часу JSON в java.util.Dateоб'єкти. JSON, що надходить, виглядає так.

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

Як я можу сказати Retrofit або Gson перетворити ці рядки на java.util.Date objects?


Схоже на дублікат цього запитання .
Davmrtl

@Davmrtl: це не однаково, оскільки тут є два різні формати дат. Тож вимагає іншого підходу.
giampaolo

Відповіді:


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

Або еквівалент Котліна:

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)

Ви можете налаштувати власний синтаксичний аналізатор Gson на модернізацію. Більше тут: Веб-сайт про модернізацію

Подивіться на відповідь Ондрежу, щоб побачити, як реалізувати це в модернізації 2.


8
Будь ласка, коротко поясніть свій код, щоб він був кориснішим для OP та інших читачів.
Мохіт Джейн,

Як я можу зробити це для всіх своїх запитів? Так що додати його до мого ServiceGenerator?
Zapnologica

4
Це насправді не стосується мови. 2016-02-11T13:42:14.401Zотримує десеріалізацію для об'єкта дати, який призначений для 13:42:14моєї мови.
Аліреза Міріан

Я спробував це, завантажені елементи, здається, працюють зараз. Але коли я публікую Json Object на сервері. Тоді це видалить мій часовий пояс?
Zapnologica

Після впровадження цього рішення я витратив стільки часу на виявлення проблеми зі збоєм програми без жодної причини. Коли я скопіював та вставив формат dete, щось змінило лапки з 'T' на 'T' - є інше - уважно спостерігайте!
Лукаш Тарашка

86

Відповідь @ 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();

2
Слідкуйте за лапками під час копіювання формату дати - у мене була проблема з цим!
Лукаш Тарашка

14

Ось як я це зробив:

Створіть клас 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;
}

Дякую! Мені довелося лише перейти з return df.parse(json.getAsString()); на long timeStamp = Long.parseLong(json.getAsString()); return new java.util.Date(timeStamp);
Хуан Саравія

Чудово! Дякую!
NickUnuchek

7

Gson може обробляти лише один формат дати та часу (ті, що вказані у конструкторі), а також iso8601, якщо синтаксичний аналіз за власним форматом неможливий. Отже, рішенням може бути написання власного десериалізатора. Для вирішення вашої проблеми я визначив:

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

}

за допомогою цього десериалізатора:

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

}

Останній крок - це створення Gsonекземпляра з правильним адаптером:

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

}

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

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

1
Що робити, якщо у мене всередині Foo 15-20 полів (включаючи власні об’єкти), мені доведеться десериалізувати їх вручну, правильно. Тоді це вбиває мету Гсона.
Фарід

1

Цілком буквально, якщо у вас вже є об’єкт Date з ім’ям «created_at» у класі, який ви створюєте, це дуже просто:

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

І готово. не потрібно складного перевизначення.


Вам слід було спробувати це з Модернізацією спочатку перед тим, як "І все готово. Не потрібно складних змін".
Фарід

1

Ви можете визначити два нових класи, як це:

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

0

Це не відповідає безпосередньо на поставлене запитання, але, на мій погляд, це "сучасний рівень", якщо кодер має повну свободу вибору способу вирішення проблеми.

Перш за все, це не найкраще рішення використовувати java.util.Date. Причина полягає в тому, що ці класи не мали ідеальної поведінки в деяких кутових випадках, тому там, де їх замінює клас Java Instant тощо, перевірте відповідь Базіля Бурке в цьому запитанні SO: Створення об’єктів Date у Kotlin для рівня API менше або дорівнює 16

Отже, я використовував миттєвий клас 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()
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.