diff --git a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java index 703e4802..121aa994 100644 --- a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java +++ b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java @@ -17,7 +17,6 @@ package com.google.gson; import com.google.gson.internal.$Types; - import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -301,6 +300,10 @@ final class DefaultTypeAdapters { return DEFAULT_INSTANCE_CREATORS; } + /** + * This type adapter supports three subclasses of date: Date, Timestamp, and + * java.sql.Date. + */ static final class DefaultDateTypeAdapter implements JsonSerializer, JsonDeserializer { private final DateFormat enUsFormat; private final DateFormat localFormat; @@ -345,6 +348,19 @@ final class DefaultTypeAdapters { if (!(json instanceof JsonPrimitive)) { throw new JsonParseException("The date should be a string value"); } + Date date = deserializeToDate(json); + if (typeOfT == Date.class) { + return date; + } else if (typeOfT == Timestamp.class) { + return new Timestamp(date.getTime()); + } else if (typeOfT == java.sql.Date.class) { + return new java.sql.Date(date.getTime()); + } else { + throw new IllegalArgumentException(getClass() + " cannot deserialize to " + typeOfT); + } + } + + private Date deserializeToDate(JsonElement json) { synchronized (localFormat) { try { return localFormat.parse(json.getAsString()); diff --git a/gson/src/main/java/com/google/gson/GsonBuilder.java b/gson/src/main/java/com/google/gson/GsonBuilder.java index d754ff10..c9d672b4 100644 --- a/gson/src/main/java/com/google/gson/GsonBuilder.java +++ b/gson/src/main/java/com/google/gson/GsonBuilder.java @@ -18,6 +18,7 @@ package com.google.gson; import com.google.gson.internal.$Preconditions; import java.lang.reflect.Type; +import java.sql.Timestamp; import java.text.DateFormat; import java.util.Arrays; import java.util.Date; @@ -400,6 +401,9 @@ public final class GsonBuilder { * call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation * will be used to decide the serialization format. * + *

The date format will be used to serialize and deserialize {@link java.util.Date}, {@link + * java.sql.Timestamp} and {@link java.sql.Date}. + * *

Note that this pattern must abide by the convention provided by {@code SimpleDateFormat} * class. See the documentation in {@link java.text.SimpleDateFormat} for more information on * valid date and time patterns.

@@ -679,12 +683,19 @@ public final class GsonBuilder { } if (dateTypeAdapter != null) { - if (!serializers.hasSpecificHandlerFor(Date.class)) { - serializers.register(Date.class, dateTypeAdapter); - } - if (!deserializers.hasSpecificHandlerFor(Date.class)) { - deserializers.register(Date.class, dateTypeAdapter); - } + registerIfAbsent(Date.class, serializers, dateTypeAdapter); + registerIfAbsent(Date.class, deserializers, dateTypeAdapter); + registerIfAbsent(Timestamp.class, serializers, dateTypeAdapter); + registerIfAbsent(Timestamp.class, deserializers, dateTypeAdapter); + registerIfAbsent(java.sql.Date.class, serializers, dateTypeAdapter); + registerIfAbsent(java.sql.Date.class, deserializers, dateTypeAdapter); + } + } + + private static void registerIfAbsent(Class type, + ParameterizedTypeHandlerMap adapters, T adapter) { + if (!adapters.hasSpecificHandlerFor(type)) { + adapters.register(type, adapter); } } } diff --git a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java index e130c5b5..55921595 100644 --- a/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java +++ b/gson/src/test/java/com/google/gson/functional/DefaultTypeAdaptersTest.java @@ -15,6 +15,13 @@ */ package com.google.gson.functional; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; @@ -36,17 +43,8 @@ import java.util.Set; import java.util.TimeZone; import java.util.TreeSet; import java.util.UUID; - import junit.framework.TestCase; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; -import com.google.gson.reflect.TypeToken; - /** * Functional test for Json serialization and deserialization for common classes for which default * support is provided in Gson. The tests for Map types are available in {@link MapTest}. @@ -380,15 +378,53 @@ public class DefaultTypeAdaptersTest extends TestCase { // http://code.google.com/p/google-gson/issues/detail?id=230 public void testDateSerializationInCollection() throws Exception { + Type listOfDates = new TypeToken>() {}.getType(); TimeZone defaultTimeZone = TimeZone.getDefault(); - TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles")); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); Locale defaultLocale = Locale.getDefault(); Locale.setDefault(Locale.US); try { Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); List dates = Arrays.asList(new Date(0)); - String json = gson.toJson(dates, new TypeToken>() {}.getType()); - assertEquals("[\"1969-12-31\"]", json); + String json = gson.toJson(dates, listOfDates); + assertEquals("[\"1970-01-01\"]", json); + assertEquals(0L, gson.>fromJson("[\"1970-01-01\"]", listOfDates).get(0).getTime()); + } finally { + TimeZone.setDefault(defaultTimeZone); + Locale.setDefault(defaultLocale); + } + } + + // http://code.google.com/p/google-gson/issues/detail?id=230 + public void testTimestampSerialization() throws Exception { + TimeZone defaultTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); + try { + Timestamp timestamp = new Timestamp(0L); + Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); + String json = gson.toJson(timestamp, Timestamp.class); + assertEquals("\"1970-01-01\"", json); + assertEquals(0, gson.fromJson("\"1970-01-01\"", Timestamp.class).getTime()); + } finally { + TimeZone.setDefault(defaultTimeZone); + Locale.setDefault(defaultLocale); + } + } + + // http://code.google.com/p/google-gson/issues/detail?id=230 + public void testSqlDateSerialization() throws Exception { + TimeZone defaultTimeZone = TimeZone.getDefault(); + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); + try { + java.sql.Date sqlDate = new java.sql.Date(0L); + Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create(); + String json = gson.toJson(sqlDate, Timestamp.class); + assertEquals("\"1970-01-01\"", json); + assertEquals(0, gson.fromJson("\"1970-01-01\"", java.sql.Date.class).getTime()); } finally { TimeZone.setDefault(defaultTimeZone); Locale.setDefault(defaultLocale);