Преобразование строковой даты в объект дает неверный индикатор часового пояса '0'

У меня есть приложение для Android, которое получает ответы Json от веб-службы. Один из ответов представляет собой строку json с датой внутри. Я получаю дату в виде числа типа "1476399300000". Когда я пытаюсь создать с ним объект, используя GSON, я получаю эту ошибку:

Не удалось проанализировать дату ["1476399300000"]: неверный индикатор часового пояса "0" (по смещению 0)

Обе стороны работают с java.util.Date

Как я могу решить эту проблему?


person Yonatan Nir    schedule 31.01.2017    source источник
comment
Вероятно, это потому, что это не метка времени с секундами, а с миллисекундами. Это может вам помочь: stackoverflow.com/questions/5671373/   -  person Nico    schedule 31.01.2017
comment
Кажется, это миллисекунды с эпохи.   -  person IQV    schedule 31.01.2017


Ответы (1)


Значение 1476399300000 выглядит как ms из начала эпохи Unix. Просто добавьте адаптер типа в свой Gson:

final class UnixEpochDateTypeAdapter
        extends TypeAdapter<Date> {

    private static final TypeAdapter<Date> unixEpochDateTypeAdapter = new UnixEpochDateTypeAdapter();

    private UnixEpochDateTypeAdapter() {
    }

    static TypeAdapter<Date> getUnixEpochDateTypeAdapter() {
        return unixEpochDateTypeAdapter;
    }

    @Override
    public Date read(final JsonReader in)
            throws IOException {
        // this is where the conversion is performed
        return new Date(in.nextLong());
    }

    @Override
    @SuppressWarnings("resource")
    public void write(final JsonWriter out, final Date value)
            throws IOException {
        // write back if necessary or throw UnsupportedOperationException
        out.value(value.getTime());
    }

}

Настройте свой экземпляр Gson:

final Gson gson = new GsonBuilder()
        .registerTypeAdapter(Date.class, getUnixEpochDateTypeAdapter())
        .create();

Экземпляры Gson потокобезопасны, как и UnixEpochDateTypeAdapter, и могут существовать как один экземпляр глобально. Пример:

final class Mapping {   
    final Date date = null; 
}
final String json = "{\"date\":1476399300000}";
final Mapping mapping = gson.fromJson(json, Mapping.class);
System.out.println(mapping.date);
System.out.println(gson.toJson(mapping));

даст следующий результат:

Пт, 14 октября, 01:55:00 EEST 2016
{"date":1476399300000}

Обратите внимание, что адаптер типа настроен на переопределение адаптера типа даты Gson по умолчанию. Поэтому вам может потребоваться более сложный анализ, чтобы определить, является ли это просто ms эпохи Unix. Также обратите внимание, что вы можете использовать JsonDeserializer, но последний работает в виде дерева JSON, в то время как адаптеры типов работают в потоковом режиме, что несколько более эффективно, вероятно, без накопления промежуточных результатов.

Редактировать:

Кроме того, это может показаться запутанным, но Gson может преобразовывать значения для примитивов. Несмотря на то, что ваша полезная нагрузка имеет строковое значение, JsonReader.nextLong() может читать строковый примитив как длинное значение. Таким образом, UnixEpochDateTypeAdapter.write должно быть out.value(String.valueOf(value.getTime()));, чтобы не изменять литералы JSON.

Редактировать

Существует также более короткое решение (работа с деревьями JSON в памяти, а не с потоковой передачей данных), которое просто:

final Gson builder = new GsonBuilder()
    .registerTypeAdapter(Date.class, new JsonDeserializer<Date>() { 
       public Date deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext context) throws JsonParseException {
          return new Date(jsonElement.getAsJsonPrimitive().getAsLong()); 
       } 
    })
    .create();
person Lyubomyr Shaydariv    schedule 31.01.2017
comment
@YonatanNir, обратите внимание, что JsonSerializer/JsonDeserializer немного отличается от TypeAdapter: первые два класса используются для преобразования деревьев JSON в значения (при условии, что они должны быть в памяти), в то время как последний работает в потоковом режиме. Я намеренно не предлагал JsonDeserializer в пользу действительно простых в реализации TypeAdapter и производительности. - person Lyubomyr Shaydariv; 01.02.2017
comment
Зарегистрируйте UnixEpochDateTypeAdapter таким образом, чтобы он был Null Safe и мог пропустить преобразование метки времени в операции записи, если во время десериализации значение равно null. Это можно сделать с помощью final Gson gson = new GsonBuilder() .registerTypeAdapter(Date.class, UnixEpochDateTypeAdapter.getUnixEpochDateTypeAdapter().nullSafe()).create() - person Satyam; 19.04.2020