【问题标题】:Custom gson deserializer for Date never gets calledDate 的自定义 gson 反序列化器永远不会被调用
【发布时间】:2014-04-15 22:36:14
【问题描述】:

据我所知,gson 不会自动将 java.util.Date 对象序列化和反序列化为 ISO 字符串,如“yyyy-MM-ddTHH:mm:ssZ”或例如“2014-04-15T18:22: 00-05:00”。因此,为了让我在客户端(使用带有 gson 的 Retrofit)和服务器之间正确通信日期,我需要将 DateFormat 指定为 gson。这是我所做的:

// code defining the creation of a RestAdapter
// ...
new GsonBuilder()
    .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
    .create()

添加 .setDateFormat 行足以让 gson 将时间戳字符串正确反序列化为 Date 对象。但是,它没有将 Date 对象序列化为时间戳字符串。所以我假设我必须像这样创建一个自定义序列化程序:

// code defining the creation of a RestAdapter
// ...
new GsonBuilder()
    .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
    .registerTypeAdapter(Date.class, new DateSerializer())
    .create()

和 DateSerializer 类:

class DateSerializer implements JsonSerializer<Date> {
    @Override
    public JsonElement serialize(Date arg0, Type arg1, JsonSerializationContext arg2) {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
        return new JsonPrimitive(df.format(arg0));
    }
}

不幸的是,序列化函数被忽略了。相反,gson 将日期格式化为字符串,例如“Tues Mar 15 18:22:00 EST 2014”。所以为了测试,我尝试将序列化函数替换为:

public JsonElement serialize(Date arg0, Type arg1, JsonSerializationContext arg2) {
    throw new RuntimeException("Serialize function called!");
}

当然,永远不会抛出 RuntimeException。

有人知道为什么我的序列化函数被忽略了吗?我想我在某处读到,对于某些类型,如果为超类定义了一个 registerTypeAdapter,则会忽略它,但由于这是 java.util.Date,如果这是问题所在,我会感到困惑。我可能只是在做一些愚蠢的事情,但我可能不太了解 Date 或 gson 以实现它。

编辑:围绕以下代码提供更多上下文:

MyApplication.java

public class MyApplication extends Application {
    public static RestAdapter restAdapter;
    public static The1Api the1Api;

    public static void createRestAdapter(String server_url){
        // enable cookies
        CookieManager cookieManager = new CookieManager();
        cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
        CookieHandler.setDefault(cookieManager);

        // create rest adapter
        restAdapter = new RestAdapter.Builder()
            .setEndpoint(server_url)
            .setConverter(new GsonConverter(new GsonBuilder()
                    .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
                    .registerTypeAdapter(Date.class, new DateSerializer())
                    .create()))
            .setLogLevel(LogLevel.FULL)
            .setLog(new ResponseInterceptor())
            .build();

        // create API
        the1Api = restAdapter.create(The1Api.class);
    }
}

The1Api.java

public interface The1Api {
    /* Chat */

    public class PublicMessage {
        String from_user;
        String message;
        Date time;
        Integer microsecond;
        Boolean in_1_percent;
    }
    public class PublicMessageList {
        Integer last_message_id;
        ArrayList<PublicMessage> messages;
    }

    @GET("/chat/get_public_messages/")
    public PublicMessageList getPublicMessages(
            @Query("last_message_id") Integer last_message_id, // optional
            @Query("since") Date since, // optional
            @Query("max") Integer max // optional
            );   

    // ...
}

LoginActivity.java

public class LoginActivity extends Activity {
    // ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Crashlytics.start(this);
        MyApplication.createRestAdapter(getString(R.string.server_url));
        setContentView(R.layout.activity_login);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState){
        super.onPostCreate(savedInstanceState);

        Thread thread = new Thread(){
            @Override
            public void run(){
                ArrayList<The1Api.PublicMessage> publicMessages = MyApplication.the1Api.getPublicMessages(null, null, null).messages;
                for (The1Api.PublicMessage m : publicMessages){
                    Log.d("The1", "[" + m.time.toString() + "] " + m.from_user + ": " + m.message);
                }
                // when the following line gets executed, my server receives a request including the date below,
                // but the server does not understand the format of the date because it does not get serialized properly
                MyApplication.the1Api.getPublicMessages(null, new Date(1000000000), null);
            }
        };
        thread.start();
    }

    // ...    
}

DateSerializer.java

class DateSerializer implements JsonSerializer<Date> {
    @Override
    public JsonElement serialize(Date arg0, Type arg1, JsonSerializationContext arg2) {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
        return new JsonPrimitive(df.format(arg0));
    }
}

编辑 2:目前还没有解决方案,但作为一种解决方法,您可以在发送之前手动将日期转换为其他格式。在 cmets Kalel 中建议将其转换为字符串,我将其转换为 Long(自 UNIX 纪元以来的秒数)。

【问题讨论】:

  • 先看SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);df.format(arg0)。如果它运行良好,则转到下一步定义您的自定义序列化程序。欲了解更多信息,请查看SimpleDateFormat
  • 当我之前尝试调试这些行时,我注意到它们从未被调用——因此我尝试用抛出 RuntimeException 来替换代码。但是,由于序列化程序永远不会被调用,即使这些行是错误的,这也不是问题,可以吗?
  • 所以问题是序列化程序永远不会被调用。如果我错了,请纠正我。
  • 你能发布你的完整代码吗?你用的是什么 JSON?返回的 Gson 是如何使用的?
  • 感谢 Kalel,应该可以!在与该项目的其他工程师交谈后,我们决定将日期时间传递为​​ long(自 UNIX 纪元以来的秒数),这似乎对我们有用。因为这只是一种解决方法,但我会留下问题以防 Retrofit/Gson 得到更新(我不确定谁应该受到责备)。

标签: java date serialization gson retrofit


【解决方案1】:

您仍然能够复制此问题吗?我看到 Gson 正确序列化和反序列化。这可能是一个改造问题(我以前没有使用过),但是查看GsonConverter 的文档,我认为除了委托给Gson 对象中提供的对象之外,它没有其他任何理由。

这是一个 SSCCE:

public class GsonDateDemo {
  public static void main(String[] args) {
    Gson gson = new GsonBuilder()
        .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
        .registerTypeAdapter(Date.class, new DateSerializer())
        .create();
    Date d = new Date(1438806977000L);

    System.out.println("Serializing "+d);
    String json = gson.toJson(d);
    System.out.println("JSON: "+json);
    Date d2 = gson.fromJson(json, Date.class);
    System.out.println("Deserialized: "+d2);
    System.out.println("Equal? "+d.equals(d2));
  }

  static class DateSerializer implements JsonSerializer<Date> {
    @Override
    public JsonElement serialize(Date arg0, Type arg1, JsonSerializationContext arg2) {
      System.out.println("Serializer received "+arg0);
      SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US);
      return new JsonPrimitive(df.format(arg0));
    }
  }
}

这个输出:

Calling: local.GsonDateDemo.main[]
Serializing Wed Aug 05 16:36:17 EDT 2015
Serializer received Wed Aug 05 16:36:17 EDT 2015
JSON: "2015-08-05T16:36:17-0400"
Deserialized: Wed Aug 05 16:36:17 EDT 2015
Equal? true

如果可以,修改此 SSCCE 以使用 Retrofit 并查看是否可以通过这种方式复制故障。

【讨论】:

  • 我已经有一段时间没有从事这个项目了;恐怕我不能试一试。如果我再次遇到类似的问题,我会回到这个。也许其他经历过这个问题的人能够分享他们的想法?
【解决方案2】:

gson 中的问题,当我们尝试使用 after call setDateFormat 反序列化时,自定义 DateDesializer 没有调用。

所以当你反序列化时不要调用setDateFormat ;)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-18
    • 2011-04-25
    • 2012-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多