Use new instances of DateTypeAdapter and TimeTypeAdapter for each GSON; this guarantees that the TimeZone and Locale are what they should be

This commit is contained in:
Jesse Wilson 2011-09-26 21:44:08 +00:00
parent 2f0fbf6bcc
commit 3b3a60d301
4 changed files with 170 additions and 100 deletions

View File

@ -16,6 +16,28 @@
package com.google.gson;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.ParameterizedTypeHandlerMap;
import com.google.gson.internal.Primitives;
import com.google.gson.internal.Streams;
import com.google.gson.internal.bind.ArrayTypeAdapter;
import com.google.gson.internal.bind.BigDecimalTypeAdapter;
import com.google.gson.internal.bind.BigIntegerTypeAdapter;
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
import com.google.gson.internal.bind.DateTypeAdapter;
import com.google.gson.internal.bind.ExcludedTypeAdapterFactory;
import com.google.gson.internal.bind.MapTypeAdapterFactory;
import com.google.gson.internal.bind.MiniGson;
import com.google.gson.internal.bind.ObjectTypeAdapter;
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
import com.google.gson.internal.bind.TimeTypeAdapter;
import com.google.gson.internal.bind.TypeAdapter;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
import java.io.EOFException;
import java.io.IOException;
import java.io.Reader;
@ -31,27 +53,6 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.ParameterizedTypeHandlerMap;
import com.google.gson.internal.Primitives;
import com.google.gson.internal.Streams;
import com.google.gson.internal.bind.ArrayTypeAdapter;
import com.google.gson.internal.bind.BigDecimalTypeAdapter;
import com.google.gson.internal.bind.BigIntegerTypeAdapter;
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
import com.google.gson.internal.bind.ExcludedTypeAdapterFactory;
import com.google.gson.internal.bind.MapTypeAdapterFactory;
import com.google.gson.internal.bind.MiniGson;
import com.google.gson.internal.bind.ObjectTypeAdapter;
import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
import com.google.gson.internal.bind.TypeAdapter;
import com.google.gson.internal.bind.TypeAdapters;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.google.gson.stream.MalformedJsonException;
/**
* This is the main class for using Gson. Gson is typically used by first constructing a
* Gson instance and then invoking {@link #toJson(Object)} or {@link #fromJson(String, Class)}
@ -255,9 +256,9 @@ public final class Gson {
.factory(TypeAdapters.LOCALE_FACTORY)
.factory(TypeAdapters.INET_ADDRESS_FACTORY)
.factory(TypeAdapters.BIT_SET_FACTORY)
.factory(TypeAdapters.DATE_FACTORY)
.factory(DateTypeAdapter.FACTORY)
.factory(TypeAdapters.CALENDAR_FACTORY)
.factory(TypeAdapters.SQL_TIME_FACTORY)
.factory(TimeTypeAdapter.FACTORY)
.factory(TypeAdapters.SQL_DATE_FACTORY)
.factory(TypeAdapters.SQL_TIMESTAMP_FACTORY)
.factory(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization))

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.internal.bind;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
/**
* Adapter for Time. Although this class appears stateless, it is not.
* DateFormat captures its time zone and locale when it is created, which gives
* this class state. DateFormat isn't thread safe either, so this class has
* to synchronize its read and write methods.
*/
public final class DateTypeAdapter extends TypeAdapter<Date> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
}
};
private final DateFormat enUsFormat
= DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
private final DateFormat localFormat
= DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
private final DateFormat iso8601Format = buildIso8601Format();
private static DateFormat buildIso8601Format() {
DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
return iso8601Format;
}
@Override public Date read(JsonReader reader) throws IOException {
return deserializeToDate(reader.nextString());
}
private synchronized Date deserializeToDate(String json) {
try {
return localFormat.parse(json);
} catch (ParseException ignored) {
}
try {
return enUsFormat.parse(json);
} catch (ParseException ignored) {
}
try {
return iso8601Format.parse(json);
} catch (ParseException e) {
throw new JsonSyntaxException(json, e);
}
}
@Override public synchronized void write(JsonWriter writer, Date value) throws IOException {
String dateFormatAsString = enUsFormat.format(value);
writer.value(dateFormatAsString);
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.gson.internal.bind;
import com.google.gson.JsonSyntaxException;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.sql.Time;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Adapter for Time. Although this class appears stateless, it is not.
* DateFormat captures its time zone and locale when it is created, which gives
* this class state. DateFormat isn't thread safe either, so this class has
* to synchronize its read and write methods.
*/
public final class TimeTypeAdapter extends TypeAdapter<Time> {
public static final Factory FACTORY = new Factory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
public <T> TypeAdapter<T> create(MiniGson context, TypeToken<T> typeToken) {
return typeToken.getRawType() == Time.class ? (TypeAdapter<T>) new TimeTypeAdapter() : null;
}
};
private final DateFormat format = new SimpleDateFormat("hh:mm:ss a");
@Override public synchronized Time read(JsonReader reader) throws IOException {
try {
Date date = format.parse(reader.nextString());
return new Time(date.getTime());
} catch (ParseException e) {
throw new JsonSyntaxException(e);
}
}
@Override public synchronized void write(JsonWriter writer, Time value) throws IOException {
writer.value(format.format(value));
}
}

View File

@ -16,12 +16,18 @@
package com.google.gson.internal.bind;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.internal.LazilyParsedNumber;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
@ -32,17 +38,8 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.StringTokenizer;
import java.util.TimeZone;
import java.util.UUID;
import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.internal.LazilyParsedNumber;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
/**
* Type adapters for basic types.
*/
@ -412,75 +409,8 @@ public final class TypeAdapters {
}
};
private static DateFormat buildIso8601Format() {
DateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
iso8601Format.setTimeZone(TimeZone.getTimeZone("UTC"));
return iso8601Format;
}
public static final TypeAdapter<Date> DATE = new TypeAdapter<Date>() {
private final DateFormat enUsFormat =
DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.US);
private final DateFormat localFormat =
DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT);
private final DateFormat iso8601Format = buildIso8601Format();
@Override
public Date read(JsonReader reader) throws IOException {
return deserializeToDate(reader.nextString());
}
private Date deserializeToDate(String json) {
synchronized (localFormat) {
try {
return localFormat.parse(json);
} catch (ParseException ignored) {
}
try {
return enUsFormat.parse(json);
} catch (ParseException ignored) {
}
try {
return iso8601Format.parse(json);
} catch (ParseException e) {
throw new JsonSyntaxException(json, e);
}
}
}
@Override
public void write(JsonWriter writer, Date value) throws IOException {
synchronized (localFormat) {
String dateFormatAsString = enUsFormat.format(value);
writer.value(dateFormatAsString);
}
}
};
public static final TypeAdapter.Factory DATE_FACTORY = newFactory(Date.class, DATE);
public static final TypeAdapter.Factory UUID_FACTORY = newFactory(UUID.class, UUID);
public static final TypeAdapter<Time> SQL_TIME = new TypeAdapter<Time>() {
private final DateFormat format = new SimpleDateFormat("hh:mm:ss a");
@Override
public Time read(JsonReader reader) throws IOException {
try {
synchronized (format) {
Date date = format.parse(reader.nextString());
return new java.sql.Time(date.getTime());
}
} catch (ParseException e) {
throw new JsonSyntaxException(e);
}
}
@Override
public void write(JsonWriter writer, Time value) throws IOException {
writer.value(format.format(value));
}
};
public static final TypeAdapter.Factory SQL_TIME_FACTORY = newFactory(Time.class, SQL_TIME);
private static final class TimestampTypeAdapter extends TypeAdapter<Timestamp> {
private final MiniGson context;
public TimestampTypeAdapter(MiniGson context) {