改装GSON将json字符串中的Date序列化为java.util.date


81

我正在对我的REST调用使用Retrofit库。我所做的大部分工作都像黄油一样顺利,但是由于某些原因,我在将JSON时间戳字符串转换为java.util.Date对象时遇到了问题。传入的JSON如下所示。

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

如何告诉Retrofit或Gson将这些字符串转换成java.util.Date objects


看起来像是这个问题的重复。
2013年

@Davmrtl:这不一样,因为这里有两种不同的日期格式。因此,它需要一种不同的方法。
giampaolo 2013年

Answers:


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

或等效的Kotlin:

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解析器设置为可改进。更多内容:改造网站

查看Ondreju的回复,以了解如何在改造2中实施此操作。


8
请简要说明您的代码,以使其对OP和其他读者更有用。
Mohit Jain 2014年

如何针对我的所有请求执行此操作?所以加到我的ServiceGenerator
Zapnologica 2015年

4
这实际上不适用于我。2016-02-11T13:42:14.401Z反序列化为13:42:14在我的语言环境中使用的日期对象。
Alireza Mirian'2

我已经尝试过了,下载的项目似乎现在正在工作。但是当我将Json对象发布到服务器时。然后会删除我的时区吗?
Zapnologica

实施此解决方案后,我花了很多时间来无故检测出我的应用程序崩溃的问题。当我复制并粘贴DETE格式的东西,从改变一个引号“T”到“T” -有一个不同的-仔细观看!
LukaszTaraszka '17

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
复制日期格式时请注意引号-我对此有疑问!
LukaszTaraszka '17

14

这是我的做法:

创建扩展Date的DateTime类,然后编写一个自定义反序列化器:

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);
Juan Saravia,2014年

大!谢谢!
NickUnuchek '17

7

如果无法使用自定义格式进行解析,那么Gson只能处理一种日期时间格式(在Builder中指定的格式),还可以处理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个字段(包括自定义对象),该如何手动反序列化呢?那时,这扼杀了Gson的目的。
Farid

1

从字面上看,如果您正在创建的类中已经有一个名称为“ created_at”的Date对象,则很简单:

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

这样就完成了。无需复杂的覆盖。


您应该先在Retrofit上进行尝试,然后再“完成。不需要复杂的重写”。
法里德'18

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问题中检查Basil Bourque的答案:在Kotlin中为API级别创建小于或等于16的Date对象

因此,我在Android上使用了ThreeTenABP的Instant类,并使用了Kotlin:

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()
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.