mirror of https://github.com/apache/jclouds.git
Issue 379: patched gson to override default enum parsing
This commit is contained in:
parent
1347165118
commit
2f5c52d86d
|
@ -0,0 +1,928 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 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;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
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;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import com.google.common.collect.MapMaker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of all the default type adapters ({@link JsonSerializer}s, {@link JsonDeserializer}s, and
|
||||||
|
* {@link InstanceCreator}s.
|
||||||
|
*
|
||||||
|
* <h4>Note!</h4>
|
||||||
|
* changed to edit the default behaviour of enum parsing by Adrian Cole
|
||||||
|
*
|
||||||
|
* @author Inderjeet Singh
|
||||||
|
* @author Joel Leitch
|
||||||
|
*/
|
||||||
|
final class JcloudsDefaultTypeAdapters {
|
||||||
|
|
||||||
|
private static final DefaultDateTypeAdapter DATE_TYPE_ADAPTER = new DefaultDateTypeAdapter();
|
||||||
|
private static final DefaultJavaSqlDateTypeAdapter JAVA_SQL_DATE_TYPE_ADAPTER = new DefaultJavaSqlDateTypeAdapter();
|
||||||
|
private static final DefaultTimeTypeAdapter TIME_TYPE_ADAPTER = new DefaultTimeTypeAdapter();
|
||||||
|
private static final DefaultTimestampDeserializer TIMESTAMP_DESERIALIZER = new DefaultTimestampDeserializer();
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes" })
|
||||||
|
private static final EnumTypeAdapter ENUM_TYPE_ADAPTER = new EnumTypeAdapter();
|
||||||
|
private static final UrlTypeAdapter URL_TYPE_ADAPTER = new UrlTypeAdapter();
|
||||||
|
private static final UriTypeAdapter URI_TYPE_ADAPTER = new UriTypeAdapter();
|
||||||
|
private static final UuidTypeAdapter UUUID_TYPE_ADAPTER = new UuidTypeAdapter();
|
||||||
|
private static final LocaleTypeAdapter LOCALE_TYPE_ADAPTER = new LocaleTypeAdapter();
|
||||||
|
private static final CollectionTypeAdapter COLLECTION_TYPE_ADAPTER = new CollectionTypeAdapter();
|
||||||
|
private static final MapTypeAdapter MAP_TYPE_ADAPTER = new MapTypeAdapter();
|
||||||
|
private static final BigDecimalTypeAdapter BIG_DECIMAL_TYPE_ADAPTER = new BigDecimalTypeAdapter();
|
||||||
|
private static final BigIntegerTypeAdapter BIG_INTEGER_TYPE_ADAPTER = new BigIntegerTypeAdapter();
|
||||||
|
|
||||||
|
private static final BooleanTypeAdapter BOOLEAN_TYPE_ADAPTER = new BooleanTypeAdapter();
|
||||||
|
private static final ByteTypeAdapter BYTE_TYPE_ADAPTER = new ByteTypeAdapter();
|
||||||
|
private static final CharacterTypeAdapter CHARACTER_TYPE_ADAPTER = new CharacterTypeAdapter();
|
||||||
|
private static final DoubleDeserializer DOUBLE_TYPE_ADAPTER = new DoubleDeserializer();
|
||||||
|
private static final FloatDeserializer FLOAT_TYPE_ADAPTER = new FloatDeserializer();
|
||||||
|
private static final IntegerTypeAdapter INTEGER_TYPE_ADAPTER = new IntegerTypeAdapter();
|
||||||
|
private static final LongDeserializer LONG_DESERIALIZER = new LongDeserializer();
|
||||||
|
private static final NumberTypeAdapter NUMBER_TYPE_ADAPTER = new NumberTypeAdapter();
|
||||||
|
private static final ShortTypeAdapter SHORT_TYPE_ADAPTER = new ShortTypeAdapter();
|
||||||
|
private static final StringTypeAdapter STRING_TYPE_ADAPTER = new StringTypeAdapter();
|
||||||
|
|
||||||
|
private static final PropertiesCreator PROPERTIES_CREATOR = new PropertiesCreator();
|
||||||
|
private static final TreeSetCreator TREE_SET_CREATOR = new TreeSetCreator();
|
||||||
|
private static final HashSetCreator HASH_SET_CREATOR = new HashSetCreator();
|
||||||
|
private static final GregorianCalendarTypeAdapter GREGORIAN_CALENDAR_TYPE_ADAPTER = new GregorianCalendarTypeAdapter();
|
||||||
|
|
||||||
|
// The constants DEFAULT_SERIALIZERS, DEFAULT_DESERIALIZERS, and DEFAULT_INSTANCE_CREATORS
|
||||||
|
// must be defined after the constants for the type adapters. Otherwise, the type adapter
|
||||||
|
// constants will appear as nulls.
|
||||||
|
private static final ParameterizedTypeHandlerMap<JsonSerializer<?>> DEFAULT_SERIALIZERS = createDefaultSerializers();
|
||||||
|
private static final ParameterizedTypeHandlerMap<JsonDeserializer<?>> DEFAULT_DESERIALIZERS = createDefaultDeserializers();
|
||||||
|
private static final ParameterizedTypeHandlerMap<InstanceCreator<?>> DEFAULT_INSTANCE_CREATORS = createDefaultInstanceCreators();
|
||||||
|
|
||||||
|
private static ParameterizedTypeHandlerMap<JsonSerializer<?>> createDefaultSerializers() {
|
||||||
|
ParameterizedTypeHandlerMap<JsonSerializer<?>> map = new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||||
|
|
||||||
|
map.registerForTypeHierarchy(Enum.class, ENUM_TYPE_ADAPTER);
|
||||||
|
map.register(URL.class, URL_TYPE_ADAPTER);
|
||||||
|
map.register(URI.class, URI_TYPE_ADAPTER);
|
||||||
|
map.register(UUID.class, UUUID_TYPE_ADAPTER);
|
||||||
|
map.register(Locale.class, LOCALE_TYPE_ADAPTER);
|
||||||
|
map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
|
||||||
|
map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER);
|
||||||
|
map.register(Date.class, DATE_TYPE_ADAPTER);
|
||||||
|
map.register(java.sql.Date.class, JAVA_SQL_DATE_TYPE_ADAPTER);
|
||||||
|
map.register(Timestamp.class, DATE_TYPE_ADAPTER);
|
||||||
|
map.register(Time.class, TIME_TYPE_ADAPTER);
|
||||||
|
map.register(Calendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER);
|
||||||
|
map.register(GregorianCalendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER);
|
||||||
|
map.register(BigDecimal.class, BIG_DECIMAL_TYPE_ADAPTER);
|
||||||
|
map.register(BigInteger.class, BIG_INTEGER_TYPE_ADAPTER);
|
||||||
|
|
||||||
|
// Add primitive serializers
|
||||||
|
map.register(Boolean.class, BOOLEAN_TYPE_ADAPTER);
|
||||||
|
map.register(boolean.class, BOOLEAN_TYPE_ADAPTER);
|
||||||
|
map.register(Byte.class, BYTE_TYPE_ADAPTER);
|
||||||
|
map.register(byte.class, BYTE_TYPE_ADAPTER);
|
||||||
|
map.register(Character.class, CHARACTER_TYPE_ADAPTER);
|
||||||
|
map.register(char.class, CHARACTER_TYPE_ADAPTER);
|
||||||
|
map.register(Integer.class, INTEGER_TYPE_ADAPTER);
|
||||||
|
map.register(int.class, INTEGER_TYPE_ADAPTER);
|
||||||
|
map.register(Number.class, NUMBER_TYPE_ADAPTER);
|
||||||
|
map.register(Short.class, SHORT_TYPE_ADAPTER);
|
||||||
|
map.register(short.class, SHORT_TYPE_ADAPTER);
|
||||||
|
map.register(String.class, STRING_TYPE_ADAPTER);
|
||||||
|
|
||||||
|
map.makeUnmodifiable();
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ParameterizedTypeHandlerMap<JsonDeserializer<?>> createDefaultDeserializers() {
|
||||||
|
ParameterizedTypeHandlerMap<JsonDeserializer<?>> map = new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
|
||||||
|
map.registerForTypeHierarchy(Enum.class, wrapDeserializer(ENUM_TYPE_ADAPTER));
|
||||||
|
map.register(URL.class, wrapDeserializer(URL_TYPE_ADAPTER));
|
||||||
|
map.register(URI.class, wrapDeserializer(URI_TYPE_ADAPTER));
|
||||||
|
map.register(UUID.class, wrapDeserializer(UUUID_TYPE_ADAPTER));
|
||||||
|
map.register(Locale.class, wrapDeserializer(LOCALE_TYPE_ADAPTER));
|
||||||
|
map.registerForTypeHierarchy(Collection.class, wrapDeserializer(COLLECTION_TYPE_ADAPTER));
|
||||||
|
map.registerForTypeHierarchy(Map.class, wrapDeserializer(MAP_TYPE_ADAPTER));
|
||||||
|
map.register(Date.class, wrapDeserializer(DATE_TYPE_ADAPTER));
|
||||||
|
map.register(java.sql.Date.class, wrapDeserializer(JAVA_SQL_DATE_TYPE_ADAPTER));
|
||||||
|
map.register(Timestamp.class, wrapDeserializer(TIMESTAMP_DESERIALIZER));
|
||||||
|
map.register(Time.class, wrapDeserializer(TIME_TYPE_ADAPTER));
|
||||||
|
map.register(Calendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER);
|
||||||
|
map.register(GregorianCalendar.class, GREGORIAN_CALENDAR_TYPE_ADAPTER);
|
||||||
|
map.register(BigDecimal.class, wrapDeserializer(BIG_DECIMAL_TYPE_ADAPTER));
|
||||||
|
map.register(BigInteger.class, wrapDeserializer(BIG_INTEGER_TYPE_ADAPTER));
|
||||||
|
|
||||||
|
// Add primitive deserializers
|
||||||
|
map.register(Boolean.class, wrapDeserializer(BOOLEAN_TYPE_ADAPTER));
|
||||||
|
map.register(boolean.class, wrapDeserializer(BOOLEAN_TYPE_ADAPTER));
|
||||||
|
map.register(Byte.class, wrapDeserializer(BYTE_TYPE_ADAPTER));
|
||||||
|
map.register(byte.class, wrapDeserializer(BYTE_TYPE_ADAPTER));
|
||||||
|
map.register(Character.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER));
|
||||||
|
map.register(char.class, wrapDeserializer(CHARACTER_TYPE_ADAPTER));
|
||||||
|
map.register(Double.class, wrapDeserializer(DOUBLE_TYPE_ADAPTER));
|
||||||
|
map.register(double.class, wrapDeserializer(DOUBLE_TYPE_ADAPTER));
|
||||||
|
map.register(Float.class, wrapDeserializer(FLOAT_TYPE_ADAPTER));
|
||||||
|
map.register(float.class, wrapDeserializer(FLOAT_TYPE_ADAPTER));
|
||||||
|
map.register(Integer.class, wrapDeserializer(INTEGER_TYPE_ADAPTER));
|
||||||
|
map.register(int.class, wrapDeserializer(INTEGER_TYPE_ADAPTER));
|
||||||
|
map.register(Long.class, wrapDeserializer(LONG_DESERIALIZER));
|
||||||
|
map.register(long.class, wrapDeserializer(LONG_DESERIALIZER));
|
||||||
|
map.register(Number.class, wrapDeserializer(NUMBER_TYPE_ADAPTER));
|
||||||
|
map.register(Short.class, wrapDeserializer(SHORT_TYPE_ADAPTER));
|
||||||
|
map.register(short.class, wrapDeserializer(SHORT_TYPE_ADAPTER));
|
||||||
|
map.register(String.class, wrapDeserializer(STRING_TYPE_ADAPTER));
|
||||||
|
|
||||||
|
map.makeUnmodifiable();
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ParameterizedTypeHandlerMap<InstanceCreator<?>> createDefaultInstanceCreators() {
|
||||||
|
ParameterizedTypeHandlerMap<InstanceCreator<?>> map = new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
|
||||||
|
map.registerForTypeHierarchy(Map.class, MAP_TYPE_ADAPTER);
|
||||||
|
|
||||||
|
// Add Collection type instance creators
|
||||||
|
map.registerForTypeHierarchy(Collection.class, COLLECTION_TYPE_ADAPTER);
|
||||||
|
|
||||||
|
map.registerForTypeHierarchy(Set.class, HASH_SET_CREATOR);
|
||||||
|
map.registerForTypeHierarchy(SortedSet.class, TREE_SET_CREATOR);
|
||||||
|
map.register(Properties.class, PROPERTIES_CREATOR);
|
||||||
|
map.makeUnmodifiable();
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
private static JsonDeserializer<?> wrapDeserializer(JsonDeserializer<?> deserializer) {
|
||||||
|
return new JsonDeserializerExceptionWrapper(deserializer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers() {
|
||||||
|
return getDefaultSerializers(false, LongSerializationPolicy.DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParameterizedTypeHandlerMap<JsonSerializer<?>> getDefaultSerializers(
|
||||||
|
boolean serializeSpecialFloatingPointValues, LongSerializationPolicy longSerializationPolicy) {
|
||||||
|
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers = new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||||
|
|
||||||
|
// Double primitive
|
||||||
|
JcloudsDefaultTypeAdapters.DoubleSerializer doubleSerializer = new JcloudsDefaultTypeAdapters.DoubleSerializer(
|
||||||
|
serializeSpecialFloatingPointValues);
|
||||||
|
serializers.registerIfAbsent(Double.class, doubleSerializer);
|
||||||
|
serializers.registerIfAbsent(double.class, doubleSerializer);
|
||||||
|
|
||||||
|
// Float primitive
|
||||||
|
JcloudsDefaultTypeAdapters.FloatSerializer floatSerializer = new JcloudsDefaultTypeAdapters.FloatSerializer(
|
||||||
|
serializeSpecialFloatingPointValues);
|
||||||
|
serializers.registerIfAbsent(Float.class, floatSerializer);
|
||||||
|
serializers.registerIfAbsent(float.class, floatSerializer);
|
||||||
|
|
||||||
|
// Long primitive
|
||||||
|
JcloudsDefaultTypeAdapters.LongSerializer longSerializer = new JcloudsDefaultTypeAdapters.LongSerializer(
|
||||||
|
longSerializationPolicy);
|
||||||
|
serializers.registerIfAbsent(Long.class, longSerializer);
|
||||||
|
serializers.registerIfAbsent(long.class, longSerializer);
|
||||||
|
|
||||||
|
serializers.registerIfAbsent(DEFAULT_SERIALIZERS);
|
||||||
|
return serializers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParameterizedTypeHandlerMap<JsonDeserializer<?>> getDefaultDeserializers() {
|
||||||
|
return DEFAULT_DESERIALIZERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParameterizedTypeHandlerMap<InstanceCreator<?>> getDefaultInstanceCreators() {
|
||||||
|
return DEFAULT_INSTANCE_CREATORS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultDateTypeAdapter implements JsonSerializer<Date>, JsonDeserializer<Date> {
|
||||||
|
private final DateFormat format;
|
||||||
|
|
||||||
|
DefaultDateTypeAdapter() {
|
||||||
|
this.format = DateFormat.getDateTimeInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultDateTypeAdapter(final String datePattern) {
|
||||||
|
this.format = new SimpleDateFormat(datePattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefaultDateTypeAdapter(final int style) {
|
||||||
|
this.format = DateFormat.getDateInstance(style);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DefaultDateTypeAdapter(final int dateStyle, final int timeStyle) {
|
||||||
|
this.format = DateFormat.getDateTimeInstance(dateStyle, timeStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These methods need to be synchronized since JDK DateFormat classes are not thread-safe
|
||||||
|
// See issue 162
|
||||||
|
public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
synchronized (format) {
|
||||||
|
String dateFormatAsString = format.format(src);
|
||||||
|
return new JsonPrimitive(dateFormatAsString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
if (!(json instanceof JsonPrimitive)) {
|
||||||
|
throw new JsonParseException("The date should be a string value");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
synchronized (format) {
|
||||||
|
return format.parse(json.getAsString());
|
||||||
|
}
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new JsonParseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(DefaultDateTypeAdapter.class.getSimpleName());
|
||||||
|
sb.append('(').append(format.getClass().getSimpleName()).append(')');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultJavaSqlDateTypeAdapter implements JsonSerializer<java.sql.Date>, JsonDeserializer<java.sql.Date> {
|
||||||
|
private final DateFormat format;
|
||||||
|
|
||||||
|
DefaultJavaSqlDateTypeAdapter() {
|
||||||
|
this.format = new SimpleDateFormat("MMM d, yyyy");
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement serialize(java.sql.Date src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
synchronized (format) {
|
||||||
|
String dateFormatAsString = format.format(src);
|
||||||
|
return new JsonPrimitive(dateFormatAsString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public java.sql.Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
if (!(json instanceof JsonPrimitive)) {
|
||||||
|
throw new JsonParseException("The date should be a string value");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
synchronized (format) {
|
||||||
|
Date date = format.parse(json.getAsString());
|
||||||
|
return new java.sql.Date(date.getTime());
|
||||||
|
}
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new JsonParseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultTimestampDeserializer implements JsonDeserializer<Timestamp> {
|
||||||
|
public Timestamp deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
Date date = context.deserialize(json, Date.class);
|
||||||
|
return new Timestamp(date.getTime());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DefaultTimeTypeAdapter implements JsonSerializer<Time>, JsonDeserializer<Time> {
|
||||||
|
private final DateFormat format;
|
||||||
|
|
||||||
|
DefaultTimeTypeAdapter() {
|
||||||
|
this.format = new SimpleDateFormat("hh:mm:ss a");
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement serialize(Time src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
synchronized (format) {
|
||||||
|
String dateFormatAsString = format.format(src);
|
||||||
|
return new JsonPrimitive(dateFormatAsString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Time deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
if (!(json instanceof JsonPrimitive)) {
|
||||||
|
throw new JsonParseException("The date should be a string value");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
synchronized (format) {
|
||||||
|
Date date = format.parse(json.getAsString());
|
||||||
|
return new Time(date.getTime());
|
||||||
|
}
|
||||||
|
} catch (ParseException e) {
|
||||||
|
throw new JsonParseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GregorianCalendarTypeAdapter implements JsonSerializer<GregorianCalendar>,
|
||||||
|
JsonDeserializer<GregorianCalendar> {
|
||||||
|
|
||||||
|
private static final String YEAR = "year";
|
||||||
|
private static final String MONTH = "month";
|
||||||
|
private static final String DAY_OF_MONTH = "dayOfMonth";
|
||||||
|
private static final String HOUR_OF_DAY = "hourOfDay";
|
||||||
|
private static final String MINUTE = "minute";
|
||||||
|
private static final String SECOND = "second";
|
||||||
|
|
||||||
|
public JsonElement serialize(GregorianCalendar src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
JsonObject obj = new JsonObject();
|
||||||
|
obj.addProperty(YEAR, src.get(Calendar.YEAR));
|
||||||
|
obj.addProperty(MONTH, src.get(Calendar.MONTH));
|
||||||
|
obj.addProperty(DAY_OF_MONTH, src.get(Calendar.DAY_OF_MONTH));
|
||||||
|
obj.addProperty(HOUR_OF_DAY, src.get(Calendar.HOUR_OF_DAY));
|
||||||
|
obj.addProperty(MINUTE, src.get(Calendar.MINUTE));
|
||||||
|
obj.addProperty(SECOND, src.get(Calendar.SECOND));
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GregorianCalendar deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
JsonObject obj = json.getAsJsonObject();
|
||||||
|
int year = obj.get(YEAR).getAsInt();
|
||||||
|
int month = obj.get(MONTH).getAsInt();
|
||||||
|
int dayOfMonth = obj.get(DAY_OF_MONTH).getAsInt();
|
||||||
|
int hourOfDay = obj.get(HOUR_OF_DAY).getAsInt();
|
||||||
|
int minute = obj.get(MINUTE).getAsInt();
|
||||||
|
int second = obj.get(SECOND).getAsInt();
|
||||||
|
return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return GregorianCalendarTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static class EnumTypeAdapter<T extends Enum<T>> implements JsonSerializer<T>, JsonDeserializer<T> {
|
||||||
|
public JsonElement serialize(T src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("cast")
|
||||||
|
public T deserialize(JsonElement json, Type classOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
try {
|
||||||
|
return (T) Enum.valueOf((Class<T>) classOfT, json.getAsString());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Method converter = classToConvert.get(classOfT);
|
||||||
|
if (converter != null)
|
||||||
|
try {
|
||||||
|
return (T) converter.invoke(null, json.getAsString());
|
||||||
|
} catch (Exception e1) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static Map<Class<?>, Method> classToConvert = new MapMaker()
|
||||||
|
.makeComputingMap(new Function<Class<?>, Method>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Method apply(Class<?> from) {
|
||||||
|
try {
|
||||||
|
Method method = from.getMethod("fromValue", String.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
return method;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return EnumTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UrlTypeAdapter implements JsonSerializer<URL>, JsonDeserializer<URL> {
|
||||||
|
public JsonElement serialize(URL src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src.toExternalForm());
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
try {
|
||||||
|
return new URL(json.getAsString());
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
throw new JsonParseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return UrlTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UriTypeAdapter implements JsonSerializer<URI>, JsonDeserializer<URI> {
|
||||||
|
public JsonElement serialize(URI src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src.toASCIIString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public URI deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
try {
|
||||||
|
return new URI(json.getAsString());
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new JsonParseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return UriTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UuidTypeAdapter implements JsonSerializer<UUID>, JsonDeserializer<UUID> {
|
||||||
|
public JsonElement serialize(UUID src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return UUID.fromString(json.getAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return UuidTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LocaleTypeAdapter implements JsonSerializer<Locale>, JsonDeserializer<Locale> {
|
||||||
|
public JsonElement serialize(Locale src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Locale deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
String locale = json.getAsString();
|
||||||
|
StringTokenizer tokenizer = new StringTokenizer(locale, "_");
|
||||||
|
String language = null;
|
||||||
|
String country = null;
|
||||||
|
String variant = null;
|
||||||
|
if (tokenizer.hasMoreElements()) {
|
||||||
|
language = tokenizer.nextToken();
|
||||||
|
}
|
||||||
|
if (tokenizer.hasMoreElements()) {
|
||||||
|
country = tokenizer.nextToken();
|
||||||
|
}
|
||||||
|
if (tokenizer.hasMoreElements()) {
|
||||||
|
variant = tokenizer.nextToken();
|
||||||
|
}
|
||||||
|
if (country == null && variant == null) {
|
||||||
|
return new Locale(language);
|
||||||
|
} else if (variant == null) {
|
||||||
|
return new Locale(language, country);
|
||||||
|
} else {
|
||||||
|
return new Locale(language, country, variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return LocaleTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
private static class CollectionTypeAdapter implements JsonSerializer<Collection>, JsonDeserializer<Collection>,
|
||||||
|
InstanceCreator<Collection> {
|
||||||
|
public JsonElement serialize(Collection src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
if (src == null) {
|
||||||
|
return JsonNull.createJsonNull();
|
||||||
|
}
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
Type childGenericType = null;
|
||||||
|
if (typeOfSrc instanceof ParameterizedType) {
|
||||||
|
childGenericType = new TypeInfoCollection(typeOfSrc).getElementType();
|
||||||
|
}
|
||||||
|
for (Object child : src) {
|
||||||
|
if (child == null) {
|
||||||
|
array.add(JsonNull.createJsonNull());
|
||||||
|
} else {
|
||||||
|
Type childType = (childGenericType == null || childGenericType == Object.class) ? child.getClass()
|
||||||
|
: childGenericType;
|
||||||
|
JsonElement element = context.serialize(child, childType);
|
||||||
|
array.add(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
if (json.isJsonNull()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Use ObjectConstructor to create instance instead of hard-coding a specific type.
|
||||||
|
// This handles cases where users are using their own subclass of Collection.
|
||||||
|
Collection collection = constructCollectionType(typeOfT, context);
|
||||||
|
Type childType = new TypeInfoCollection(typeOfT).getElementType();
|
||||||
|
for (JsonElement childElement : json.getAsJsonArray()) {
|
||||||
|
if (childElement == null || childElement.isJsonNull()) {
|
||||||
|
collection.add(null);
|
||||||
|
} else {
|
||||||
|
Object value = context.deserialize(childElement, childType);
|
||||||
|
collection.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection constructCollectionType(Type collectionType, JsonDeserializationContext context) {
|
||||||
|
JsonDeserializationContextDefault contextImpl = (JsonDeserializationContextDefault) context;
|
||||||
|
ObjectConstructor objectConstructor = contextImpl.getObjectConstructor();
|
||||||
|
return (Collection) objectConstructor.construct(collectionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection createInstance(Type type) {
|
||||||
|
return new LinkedList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PropertiesCreator implements InstanceCreator<Properties> {
|
||||||
|
public Properties createInstance(Type type) {
|
||||||
|
return new Properties();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
static class MapTypeAdapter implements JsonSerializer<Map>, JsonDeserializer<Map>, InstanceCreator<Map> {
|
||||||
|
|
||||||
|
public JsonElement serialize(Map src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
JsonObject map = new JsonObject();
|
||||||
|
Type childGenericType = null;
|
||||||
|
if (typeOfSrc instanceof ParameterizedType) {
|
||||||
|
childGenericType = new TypeInfoMap(typeOfSrc).getValueType();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry entry : (Set<Map.Entry>) src.entrySet()) {
|
||||||
|
Object value = entry.getValue();
|
||||||
|
|
||||||
|
JsonElement valueElement;
|
||||||
|
if (value == null) {
|
||||||
|
valueElement = JsonNull.createJsonNull();
|
||||||
|
} else {
|
||||||
|
Type childType = (childGenericType == null) ? value.getClass() : childGenericType;
|
||||||
|
valueElement = context.serialize(value, childType);
|
||||||
|
}
|
||||||
|
map.add(String.valueOf(entry.getKey()), valueElement);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
// Use ObjectConstructor to create instance instead of hard-coding a specific type.
|
||||||
|
// This handles cases where users are using their own subclass of Map.
|
||||||
|
Map<Object, Object> map = constructMapType(typeOfT, context);
|
||||||
|
TypeInfoMap mapTypeInfo = new TypeInfoMap(typeOfT);
|
||||||
|
for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject().entrySet()) {
|
||||||
|
Object key = context.deserialize(new JsonPrimitive(entry.getKey()), mapTypeInfo.getKeyType());
|
||||||
|
Object value = context.deserialize(entry.getValue(), mapTypeInfo.getValueType());
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map constructMapType(Type mapType, JsonDeserializationContext context) {
|
||||||
|
JsonDeserializationContextDefault contextImpl = (JsonDeserializationContextDefault) context;
|
||||||
|
ObjectConstructor objectConstructor = contextImpl.getObjectConstructor();
|
||||||
|
return (Map) objectConstructor.construct(mapType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map createInstance(Type type) {
|
||||||
|
return new LinkedHashMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return MapTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BigDecimalTypeAdapter implements JsonSerializer<BigDecimal>, JsonDeserializer<BigDecimal> {
|
||||||
|
public JsonElement serialize(BigDecimal src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigDecimal deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsBigDecimal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return BigDecimalTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BigIntegerTypeAdapter implements JsonSerializer<BigInteger>, JsonDeserializer<BigInteger> {
|
||||||
|
|
||||||
|
public JsonElement serialize(BigInteger src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BigInteger deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsBigInteger();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return BigIntegerTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class NumberTypeAdapter implements JsonSerializer<Number>, JsonDeserializer<Number> {
|
||||||
|
public JsonElement serialize(Number src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Number deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return NumberTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LongSerializer implements JsonSerializer<Long> {
|
||||||
|
private final LongSerializationPolicy longSerializationPolicy;
|
||||||
|
|
||||||
|
private LongSerializer(LongSerializationPolicy longSerializationPolicy) {
|
||||||
|
this.longSerializationPolicy = longSerializationPolicy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return longSerializationPolicy.serialize(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return LongSerializer.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LongDeserializer implements JsonDeserializer<Long> {
|
||||||
|
public Long deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return LongDeserializer.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class IntegerTypeAdapter implements JsonSerializer<Integer>, JsonDeserializer<Integer> {
|
||||||
|
public JsonElement serialize(Integer src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return IntegerTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ShortTypeAdapter implements JsonSerializer<Short>, JsonDeserializer<Short> {
|
||||||
|
public JsonElement serialize(Short src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Short deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ShortTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ByteTypeAdapter implements JsonSerializer<Byte>, JsonDeserializer<Byte> {
|
||||||
|
public JsonElement serialize(Byte src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Byte deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ByteTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class FloatSerializer implements JsonSerializer<Float> {
|
||||||
|
private final boolean serializeSpecialFloatingPointValues;
|
||||||
|
|
||||||
|
FloatSerializer(boolean serializeSpecialDoubleValues) {
|
||||||
|
this.serializeSpecialFloatingPointValues = serializeSpecialDoubleValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement serialize(Float src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
if (!serializeSpecialFloatingPointValues) {
|
||||||
|
if (Float.isNaN(src) || Float.isInfinite(src)) {
|
||||||
|
throw new IllegalArgumentException(src
|
||||||
|
+ " is not a valid float value as per JSON specification. To override this"
|
||||||
|
+ " behavior, use GsonBuilder.serializeSpecialFloatingPointValues() method.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class FloatDeserializer implements JsonDeserializer<Float> {
|
||||||
|
public Float deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsFloat();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return FloatDeserializer.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class DoubleSerializer implements JsonSerializer<Double> {
|
||||||
|
private final boolean serializeSpecialFloatingPointValues;
|
||||||
|
|
||||||
|
DoubleSerializer(boolean serializeSpecialDoubleValues) {
|
||||||
|
this.serializeSpecialFloatingPointValues = serializeSpecialDoubleValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
if (!serializeSpecialFloatingPointValues) {
|
||||||
|
if (Double.isNaN(src) || Double.isInfinite(src)) {
|
||||||
|
throw new IllegalArgumentException(src
|
||||||
|
+ " is not a valid double value as per JSON specification. To override this"
|
||||||
|
+ " behavior, use GsonBuilder.serializeSpecialDoubleValues() method.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class DoubleDeserializer implements JsonDeserializer<Double> {
|
||||||
|
public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsDouble();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return DoubleDeserializer.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CharacterTypeAdapter implements JsonSerializer<Character>, JsonDeserializer<Character> {
|
||||||
|
public JsonElement serialize(Character src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Character deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsCharacter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return CharacterTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StringTypeAdapter implements JsonSerializer<String>, JsonDeserializer<String> {
|
||||||
|
public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return StringTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BooleanTypeAdapter implements JsonSerializer<Boolean>, JsonDeserializer<Boolean> {
|
||||||
|
public JsonElement serialize(Boolean src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return json.getAsBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return BooleanTypeAdapter.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TreeSetCreator implements InstanceCreator<TreeSet<?>> {
|
||||||
|
public TreeSet<?> createInstance(Type type) {
|
||||||
|
return new TreeSet<Object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return TreeSetCreator.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class HashSetCreator implements InstanceCreator<HashSet<?>> {
|
||||||
|
public HashSet<?> createInstance(Type type) {
|
||||||
|
return new HashSet<Object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return HashSetCreator.class.getSimpleName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,591 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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.
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 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;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.google.gson.JcloudsDefaultTypeAdapters.DefaultDateTypeAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Use this builder to construct a {@link Gson} instance when you need to set configuration
|
||||||
|
* options other than the default. For {@link Gson} with default configuration, it is simpler to
|
||||||
|
* use {@code new Gson()}. {@code JcloudsGsonBuilder} is best used by creating it, and then invoking its
|
||||||
|
* various configuration methods, and finally calling create.</p>
|
||||||
|
*
|
||||||
|
* <p>The following is an example shows how to use the {@code JcloudsGsonBuilder} to construct a Gson
|
||||||
|
* instance:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Gson gson = new JcloudsGsonBuilder()
|
||||||
|
* .registerTypeAdapter(Id.class, new IdTypeAdapter())
|
||||||
|
* .serializeNulls()
|
||||||
|
* .setDateFormat(DateFormat.LONG)
|
||||||
|
* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)
|
||||||
|
* .setPrettyPrinting()
|
||||||
|
* .setVersion(1.0)
|
||||||
|
* .create();
|
||||||
|
* </pre></p>
|
||||||
|
*
|
||||||
|
* <p>NOTE: the order of invocation of configuration methods does not matter.</p>
|
||||||
|
*
|
||||||
|
* @author Inderjeet Singh
|
||||||
|
* @author Joel Leitch
|
||||||
|
*/
|
||||||
|
public final class JcloudsGsonBuilder {
|
||||||
|
private static final InnerClassExclusionStrategy innerClassExclusionStrategy =
|
||||||
|
new InnerClassExclusionStrategy();
|
||||||
|
private static final ExposeAnnotationSerializationExclusionStrategy
|
||||||
|
exposeAnnotationSerializationExclusionStrategy =
|
||||||
|
new ExposeAnnotationSerializationExclusionStrategy();
|
||||||
|
private static final ExposeAnnotationDeserializationExclusionStrategy
|
||||||
|
exposeAnnotationDeserializationExclusionStrategy =
|
||||||
|
new ExposeAnnotationDeserializationExclusionStrategy();
|
||||||
|
|
||||||
|
private final Collection<ExclusionStrategy> exclusionStrategies =
|
||||||
|
new HashSet<ExclusionStrategy>();
|
||||||
|
|
||||||
|
private double ignoreVersionsAfter;
|
||||||
|
private ModifierBasedExclusionStrategy modifierBasedExclusionStrategy;
|
||||||
|
private boolean serializeInnerClasses;
|
||||||
|
private boolean excludeFieldsWithoutExposeAnnotation;
|
||||||
|
private LongSerializationPolicy longSerializationPolicy;
|
||||||
|
private FieldNamingStrategy2 fieldNamingPolicy;
|
||||||
|
private final ParameterizedTypeHandlerMap<InstanceCreator<?>> instanceCreators;
|
||||||
|
private final ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers;
|
||||||
|
private final ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers;
|
||||||
|
private boolean serializeNulls;
|
||||||
|
private String datePattern;
|
||||||
|
private int dateStyle;
|
||||||
|
private int timeStyle;
|
||||||
|
private boolean serializeSpecialFloatingPointValues;
|
||||||
|
private boolean escapeHtmlChars;
|
||||||
|
private boolean prettyPrinting;
|
||||||
|
private boolean generateNonExecutableJson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a JcloudsGsonBuilder instance that can be used to build Gson with various configuration
|
||||||
|
* settings. JcloudsGsonBuilder follows the builder pattern, and it is typically used by first
|
||||||
|
* invoking various configuration methods to set desired options, and finally calling
|
||||||
|
* {@link #create()}.
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder() {
|
||||||
|
// add default exclusion strategies
|
||||||
|
exclusionStrategies.add(Gson.DEFAULT_ANON_LOCAL_CLASS_EXCLUSION_STRATEGY);
|
||||||
|
exclusionStrategies.add(Gson.DEFAULT_SYNTHETIC_FIELD_EXCLUSION_STRATEGY);
|
||||||
|
|
||||||
|
// setup default values
|
||||||
|
ignoreVersionsAfter = VersionConstants.IGNORE_VERSIONS;
|
||||||
|
serializeInnerClasses = true;
|
||||||
|
prettyPrinting = false;
|
||||||
|
escapeHtmlChars = true;
|
||||||
|
modifierBasedExclusionStrategy = Gson.DEFAULT_MODIFIER_BASED_EXCLUSION_STRATEGY;
|
||||||
|
excludeFieldsWithoutExposeAnnotation = false;
|
||||||
|
longSerializationPolicy = LongSerializationPolicy.DEFAULT;
|
||||||
|
fieldNamingPolicy = Gson.DEFAULT_NAMING_POLICY;
|
||||||
|
instanceCreators = new ParameterizedTypeHandlerMap<InstanceCreator<?>>();
|
||||||
|
serializers = new ParameterizedTypeHandlerMap<JsonSerializer<?>>();
|
||||||
|
deserializers = new ParameterizedTypeHandlerMap<JsonDeserializer<?>>();
|
||||||
|
serializeNulls = false;
|
||||||
|
dateStyle = DateFormat.DEFAULT;
|
||||||
|
timeStyle = DateFormat.DEFAULT;
|
||||||
|
serializeSpecialFloatingPointValues = false;
|
||||||
|
generateNonExecutableJson = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to enable versioning support.
|
||||||
|
*
|
||||||
|
* @param ignoreVersionsAfter any field or type marked with a version higher than this value
|
||||||
|
* are ignored during serialization or deserialization.
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setVersion(double ignoreVersionsAfter) {
|
||||||
|
this.ignoreVersionsAfter = ignoreVersionsAfter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to excludes all class fields that have the specified modifiers. By default,
|
||||||
|
* Gson will exclude all fields marked transient or static. This method will override that
|
||||||
|
* behavior.
|
||||||
|
*
|
||||||
|
* @param modifiers the field modifiers. You must use the modifiers specified in the
|
||||||
|
* {@link java.lang.reflect.Modifier} class. For example,
|
||||||
|
* {@link java.lang.reflect.Modifier#TRANSIENT},
|
||||||
|
* {@link java.lang.reflect.Modifier#STATIC}.
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder excludeFieldsWithModifiers(int... modifiers) {
|
||||||
|
modifierBasedExclusionStrategy = new ModifierBasedExclusionStrategy(modifiers);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the output JSON non-executable in Javascript by prefixing the generated JSON with some
|
||||||
|
* special text. This prevents attacks from third-party sites through script sourcing. See
|
||||||
|
* <a href="http://code.google.com/p/google-gson/issues/detail?id=42">Gson Issue 42</a>
|
||||||
|
* for details.
|
||||||
|
*
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder generateNonExecutableJson() {
|
||||||
|
this.generateNonExecutableJson = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to exclude all fields from consideration for serialization or deserialization
|
||||||
|
* that do not have the {@link com.google.gson.annotations.Expose} annotation.
|
||||||
|
*
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder excludeFieldsWithoutExposeAnnotation() {
|
||||||
|
excludeFieldsWithoutExposeAnnotation = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure Gson to serialize null fields. By default, Gson omits all fields that are null
|
||||||
|
* during serialization.
|
||||||
|
*
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder serializeNulls() {
|
||||||
|
this.serializeNulls = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to exclude inner classes during serialization.
|
||||||
|
*
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder disableInnerClassSerialization() {
|
||||||
|
serializeInnerClasses = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to apply a specific serialization policy for {@code Long} and {@code long}
|
||||||
|
* objects.
|
||||||
|
*
|
||||||
|
* @param serializationPolicy the particular policy to use for serializing longs.
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setLongSerializationPolicy(LongSerializationPolicy serializationPolicy) {
|
||||||
|
this.longSerializationPolicy = serializationPolicy;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to apply a specific naming policy to an object's field during serialization
|
||||||
|
* and deserialization.
|
||||||
|
*
|
||||||
|
* @param namingConvention the JSON field naming convention to use for serialization and
|
||||||
|
* deserialization.
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setFieldNamingPolicy(FieldNamingPolicy namingConvention) {
|
||||||
|
return setFieldNamingStrategy(namingConvention.getFieldNamingPolicy());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to apply a specific naming policy strategy to an object's field during
|
||||||
|
* serialization and deserialization.
|
||||||
|
*
|
||||||
|
* @param fieldNamingStrategy the actual naming strategy to apply to the fields
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setFieldNamingStrategy(FieldNamingStrategy fieldNamingStrategy) {
|
||||||
|
return setFieldNamingStrategy(new FieldNamingStrategy2Adapter(fieldNamingStrategy));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to apply a specific naming policy strategy to an object's field during
|
||||||
|
* serialization and deserialization.
|
||||||
|
*
|
||||||
|
* @param fieldNamingStrategy the actual naming strategy to apply to the fields
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
JcloudsGsonBuilder setFieldNamingStrategy(FieldNamingStrategy2 fieldNamingStrategy) {
|
||||||
|
this.fieldNamingPolicy =
|
||||||
|
new SerializedNameAnnotationInterceptingNamingPolicy(fieldNamingStrategy);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to apply a set of exclusion strategies during both serialization and
|
||||||
|
* deserialization. Each of the {@code strategies} will be applied as a disjunction rule.
|
||||||
|
* This means that if one of the {@code strategies} suggests that a field (or class) should be
|
||||||
|
* skipped then that field (or object) is skipped during serializaiton/deserialization.
|
||||||
|
*
|
||||||
|
* @param strategies the set of strategy object to apply during object (de)serialization.
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.4
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setExclusionStrategies(ExclusionStrategy... strategies) {
|
||||||
|
for (ExclusionStrategy strategy : strategies) {
|
||||||
|
exclusionStrategies.add(strategy);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to output Json that fits in a page for pretty printing. This option only
|
||||||
|
* affects Json serialization.
|
||||||
|
*
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setPrettyPrinting() {
|
||||||
|
prettyPrinting = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* By default, Gson escapes HTML characters such as < > etc. Use this option to configure
|
||||||
|
* Gson to pass-through HTML characters as is.
|
||||||
|
*
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder disableHtmlEscaping() {
|
||||||
|
this.escapeHtmlChars = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to serialize {@code Date} objects according to the pattern provided. You can
|
||||||
|
* call this method or {@link #setDateFormat(int)} multiple times, but only the last invocation
|
||||||
|
* will be used to decide the serialization format.
|
||||||
|
*
|
||||||
|
* <p>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.</p>
|
||||||
|
*
|
||||||
|
* @param pattern the pattern that dates will be serialized/deserialized to/from
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setDateFormat(String pattern) {
|
||||||
|
// TODO(Joel): Make this fail fast if it is an invalid date format
|
||||||
|
this.datePattern = pattern;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to to serialize {@code Date} objects according to the style value provided.
|
||||||
|
* You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
|
||||||
|
* invocation will be used to decide the serialization format.
|
||||||
|
*
|
||||||
|
* <p>Note that this style value should be one of the predefined constants in the
|
||||||
|
* {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
|
||||||
|
* information on the valid style constants.</p>
|
||||||
|
*
|
||||||
|
* @param style the predefined date style that date objects will be serialized/deserialized
|
||||||
|
* to/from
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setDateFormat(int style) {
|
||||||
|
this.dateStyle = style;
|
||||||
|
this.datePattern = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to to serialize {@code Date} objects according to the style value provided.
|
||||||
|
* You can call this method or {@link #setDateFormat(String)} multiple times, but only the last
|
||||||
|
* invocation will be used to decide the serialization format.
|
||||||
|
*
|
||||||
|
* <p>Note that this style value should be one of the predefined constants in the
|
||||||
|
* {@code DateFormat} class. See the documentation in {@link java.text.DateFormat} for more
|
||||||
|
* information on the valid style constants.</p>
|
||||||
|
*
|
||||||
|
* @param dateStyle the predefined date style that date objects will be serialized/deserialized
|
||||||
|
* to/from
|
||||||
|
* @param timeStyle the predefined style for the time portion of the date objects
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder setDateFormat(int dateStyle, int timeStyle) {
|
||||||
|
this.dateStyle = dateStyle;
|
||||||
|
this.timeStyle = timeStyle;
|
||||||
|
this.datePattern = null;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson for custom serialization or deserialization. This method combines the
|
||||||
|
* registration of an {@link InstanceCreator}, {@link JsonSerializer}, and a
|
||||||
|
* {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
|
||||||
|
* all the required interfaces for custom serialization with Gson. If an instance creator,
|
||||||
|
* serializer or deserializer was previously registered for the specified {@code type}, it is
|
||||||
|
* overwritten.
|
||||||
|
*
|
||||||
|
* @param type the type definition for the type adapter being registered
|
||||||
|
* @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
|
||||||
|
* {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
|
||||||
|
Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
|
||||||
|
|| typeAdapter instanceof JsonDeserializer<?> || typeAdapter instanceof InstanceCreator<?>);
|
||||||
|
if (typeAdapter instanceof InstanceCreator<?>) {
|
||||||
|
registerInstanceCreator(type, (InstanceCreator<?>) typeAdapter);
|
||||||
|
}
|
||||||
|
if (typeAdapter instanceof JsonSerializer<?>) {
|
||||||
|
registerSerializer(type, (JsonSerializer<?>) typeAdapter);
|
||||||
|
}
|
||||||
|
if (typeAdapter instanceof JsonDeserializer<?>) {
|
||||||
|
registerDeserializer(type, (JsonDeserializer<?>) typeAdapter);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to use a custom {@link InstanceCreator} for the specified type. If an instance
|
||||||
|
* creator was previously registered for the specified class, it is overwritten. Since this method
|
||||||
|
* takes a type instead of a Class object, it can be used to register a specific handler for a
|
||||||
|
* generic type corresponding to a raw type.
|
||||||
|
*
|
||||||
|
* @param <T> the type for which instance creator is being registered
|
||||||
|
* @param typeOfT The Type definition for T
|
||||||
|
* @param instanceCreator the instance creator for T
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
private <T> JcloudsGsonBuilder registerInstanceCreator(Type typeOfT,
|
||||||
|
InstanceCreator<? extends T> instanceCreator) {
|
||||||
|
instanceCreators.register(typeOfT, instanceCreator);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to use a custom JSON serializer for the specified type. You should use this
|
||||||
|
* method if you want to register different serializers for different generic types corresponding
|
||||||
|
* to a raw type.
|
||||||
|
*
|
||||||
|
* @param <T> the type for which the serializer is being registered
|
||||||
|
* @param typeOfT The type definition for T
|
||||||
|
* @param serializer the custom serializer
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
private <T> JcloudsGsonBuilder registerSerializer(Type typeOfT, final JsonSerializer<T> serializer) {
|
||||||
|
serializers.register(typeOfT, serializer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson to use a custom JSON deserializer for the specified type. You should use this
|
||||||
|
* method if you want to register different deserializers for different generic types
|
||||||
|
* corresponding to a raw type.
|
||||||
|
*
|
||||||
|
* @param <T> the type for which the deserializer is being registered
|
||||||
|
* @param typeOfT The type definition for T
|
||||||
|
* @param deserializer the custom deserializer
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
*/
|
||||||
|
private <T> JcloudsGsonBuilder registerDeserializer(Type typeOfT, JsonDeserializer<T> deserializer) {
|
||||||
|
deserializers.register(typeOfT, new JsonDeserializerExceptionWrapper<T>(deserializer));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
|
||||||
|
* This method combines the registration of an {@link InstanceCreator}, {@link JsonSerializer},
|
||||||
|
* and a {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter}
|
||||||
|
* implements all the required interfaces for custom serialization with Gson.
|
||||||
|
* If an instance creator, serializer or deserializer was previously registered for the specified
|
||||||
|
* type hierarchy, it is overwritten. If an instance creator, serializer or deserializer is
|
||||||
|
* registered for a specific type in the type hierarchy, it will be invoked instead of the one
|
||||||
|
* registered for the type hierarchy.
|
||||||
|
*
|
||||||
|
* @param baseType the class definition for the type adapter being registered for the base class
|
||||||
|
* or interface
|
||||||
|
* @param typeAdapter This object must implement at least one of the {@link InstanceCreator},
|
||||||
|
* {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.5
|
||||||
|
*/
|
||||||
|
JcloudsGsonBuilder registerTypeHierarchyAdapter(Class<?> baseType, Object typeAdapter) {
|
||||||
|
Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
|
||||||
|
|| typeAdapter instanceof JsonDeserializer<?> || typeAdapter instanceof InstanceCreator<?>);
|
||||||
|
if (typeAdapter instanceof InstanceCreator<?>) {
|
||||||
|
registerInstanceCreatorForTypeHierarchy(baseType, (InstanceCreator<?>) typeAdapter);
|
||||||
|
}
|
||||||
|
if (typeAdapter instanceof JsonSerializer<?>) {
|
||||||
|
registerSerializerForTypeHierarchy(baseType, (JsonSerializer<?>) typeAdapter);
|
||||||
|
}
|
||||||
|
if (typeAdapter instanceof JsonDeserializer<?>) {
|
||||||
|
registerDeserializerForTypeHierarchy(baseType, (JsonDeserializer<?>) typeAdapter);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> JcloudsGsonBuilder registerInstanceCreatorForTypeHierarchy(Class<?> classOfT,
|
||||||
|
InstanceCreator<? extends T> instanceCreator) {
|
||||||
|
instanceCreators.registerForTypeHierarchy(classOfT, instanceCreator);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> JcloudsGsonBuilder registerSerializerForTypeHierarchy(Class<?> classOfT,
|
||||||
|
final JsonSerializer<T> serializer) {
|
||||||
|
serializers.registerForTypeHierarchy(classOfT, serializer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> JcloudsGsonBuilder registerDeserializerForTypeHierarchy(Class<?> classOfT,
|
||||||
|
JsonDeserializer<T> deserializer) {
|
||||||
|
deserializers.registerForTypeHierarchy(classOfT,
|
||||||
|
new JsonDeserializerExceptionWrapper<T>(deserializer));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Section 2.4 of <a href="http://www.ietf.org/rfc/rfc4627.txt">JSON specification</a> disallows
|
||||||
|
* special double values (NaN, Infinity, -Infinity). However,
|
||||||
|
* <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf">Javascript
|
||||||
|
* specification</a> (see section 4.3.20, 4.3.22, 4.3.23) allows these values as valid Javascript
|
||||||
|
* values. Moreover, most JavaScript engines will accept these special values in JSON without
|
||||||
|
* problem. So, at a practical level, it makes sense to accept these values as valid JSON even
|
||||||
|
* though JSON specification disallows them.
|
||||||
|
*
|
||||||
|
* <p>Gson always accepts these special values during deserialization. However, it outputs
|
||||||
|
* strictly compliant JSON. Hence, if it encounters a float value {@link Float#NaN},
|
||||||
|
* {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, or a double value
|
||||||
|
* {@link Double#NaN}, {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, it
|
||||||
|
* will throw an {@link IllegalArgumentException}. This method provides a way to override the
|
||||||
|
* default behavior when you know that the JSON receiver will be able to handle these special
|
||||||
|
* values.
|
||||||
|
*
|
||||||
|
* @return a reference to this {@code JcloudsGsonBuilder} object to fulfill the "Builder" pattern
|
||||||
|
* @since 1.3
|
||||||
|
*/
|
||||||
|
public JcloudsGsonBuilder serializeSpecialFloatingPointValues() {
|
||||||
|
this.serializeSpecialFloatingPointValues = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a {@link Gson} instance based on the current configuration. This method is free of
|
||||||
|
* side-effects to this {@code JcloudsGsonBuilder} instance and hence can be called multiple times.
|
||||||
|
*
|
||||||
|
* @return an instance of Gson configured with the options currently set in this builder
|
||||||
|
*/
|
||||||
|
public Gson create() {
|
||||||
|
List<ExclusionStrategy> serializationStrategies =
|
||||||
|
new LinkedList<ExclusionStrategy>(exclusionStrategies);
|
||||||
|
List<ExclusionStrategy> deserializationStrategies =
|
||||||
|
new LinkedList<ExclusionStrategy>(exclusionStrategies);
|
||||||
|
|
||||||
|
serializationStrategies.add(modifierBasedExclusionStrategy);
|
||||||
|
deserializationStrategies.add(modifierBasedExclusionStrategy);
|
||||||
|
|
||||||
|
if (!serializeInnerClasses) {
|
||||||
|
serializationStrategies.add(innerClassExclusionStrategy);
|
||||||
|
deserializationStrategies.add(innerClassExclusionStrategy);
|
||||||
|
}
|
||||||
|
if (ignoreVersionsAfter != VersionConstants.IGNORE_VERSIONS) {
|
||||||
|
serializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
|
||||||
|
deserializationStrategies.add(new VersionExclusionStrategy(ignoreVersionsAfter));
|
||||||
|
}
|
||||||
|
if (excludeFieldsWithoutExposeAnnotation) {
|
||||||
|
serializationStrategies.add(exposeAnnotationSerializationExclusionStrategy);
|
||||||
|
deserializationStrategies.add(exposeAnnotationDeserializationExclusionStrategy);
|
||||||
|
}
|
||||||
|
ExclusionStrategy serializationExclusionStrategy =
|
||||||
|
new DisjunctionExclusionStrategy(serializationStrategies);
|
||||||
|
ExclusionStrategy deserializationExclusionStrategy =
|
||||||
|
new DisjunctionExclusionStrategy(deserializationStrategies);
|
||||||
|
|
||||||
|
ParameterizedTypeHandlerMap<JsonSerializer<?>> customSerializers = serializers.copyOf();
|
||||||
|
ParameterizedTypeHandlerMap<JsonDeserializer<?>> customDeserializers = deserializers.copyOf();
|
||||||
|
addTypeAdaptersForDate(datePattern, dateStyle, timeStyle, customSerializers,
|
||||||
|
customDeserializers);
|
||||||
|
|
||||||
|
customSerializers.registerIfAbsent(JcloudsDefaultTypeAdapters.getDefaultSerializers(
|
||||||
|
serializeSpecialFloatingPointValues, longSerializationPolicy));
|
||||||
|
|
||||||
|
customDeserializers.registerIfAbsent(JcloudsDefaultTypeAdapters.getDefaultDeserializers());
|
||||||
|
|
||||||
|
ParameterizedTypeHandlerMap<InstanceCreator<?>> customInstanceCreators =
|
||||||
|
instanceCreators.copyOf();
|
||||||
|
customInstanceCreators.registerIfAbsent(JcloudsDefaultTypeAdapters.getDefaultInstanceCreators());
|
||||||
|
|
||||||
|
customSerializers.makeUnmodifiable();
|
||||||
|
customDeserializers.makeUnmodifiable();
|
||||||
|
instanceCreators.makeUnmodifiable();
|
||||||
|
|
||||||
|
MappedObjectConstructor objConstructor = new MappedObjectConstructor(customInstanceCreators);
|
||||||
|
|
||||||
|
JsonFormatter formatter = prettyPrinting ?
|
||||||
|
new JsonPrintFormatter(escapeHtmlChars) : new JsonCompactFormatter(escapeHtmlChars);
|
||||||
|
Gson gson = new Gson(serializationExclusionStrategy, deserializationExclusionStrategy,
|
||||||
|
fieldNamingPolicy, objConstructor, formatter, serializeNulls, customSerializers,
|
||||||
|
customDeserializers, generateNonExecutableJson);
|
||||||
|
return gson;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addTypeAdaptersForDate(String datePattern, int dateStyle, int timeStyle,
|
||||||
|
ParameterizedTypeHandlerMap<JsonSerializer<?>> serializers,
|
||||||
|
ParameterizedTypeHandlerMap<JsonDeserializer<?>> deserializers) {
|
||||||
|
DefaultDateTypeAdapter dateTypeAdapter = null;
|
||||||
|
if (datePattern != null && !"".equals(datePattern.trim())) {
|
||||||
|
dateTypeAdapter = new DefaultDateTypeAdapter(datePattern);
|
||||||
|
} else if (dateStyle != DateFormat.DEFAULT && timeStyle != DateFormat.DEFAULT) {
|
||||||
|
dateTypeAdapter = new DefaultDateTypeAdapter(dateStyle, timeStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dateTypeAdapter != null) {
|
||||||
|
if (!serializers.hasSpecificHandlerFor(Date.class)) {
|
||||||
|
serializers.register(Date.class, dateTypeAdapter);
|
||||||
|
}
|
||||||
|
if (!deserializers.hasSpecificHandlerFor(Date.class)) {
|
||||||
|
deserializers.register(Date.class, dateTypeAdapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,8 +38,8 @@ import org.jclouds.json.internal.GsonWrapper;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
|
||||||
import com.google.gson.JcloudsCompactFormatter;
|
import com.google.gson.JcloudsCompactFormatter;
|
||||||
|
import com.google.gson.JcloudsGsonBuilder;
|
||||||
import com.google.gson.JsonDeserializationContext;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
import com.google.gson.JsonDeserializer;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
|
@ -64,9 +64,9 @@ public class GsonModule extends AbstractModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
Gson provideGson(JsonBallAdapter jsonObjectAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
|
Gson provideGson(JsonBallAdapter jsonObjectAdapter, DateAdapter adapter, ByteListAdapter byteListAdapter,
|
||||||
ByteArrayAdapter byteArrayAdapter, JsonAdapterBindings bindings) throws SecurityException,
|
ByteArrayAdapter byteArrayAdapter, JsonAdapterBindings bindings) throws ClassNotFoundException, Exception {
|
||||||
NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
|
JcloudsGsonBuilder builder = new JcloudsGsonBuilder();
|
||||||
GsonBuilder builder = new GsonBuilder();
|
|
||||||
builder.registerTypeAdapter(JsonBall.class, jsonObjectAdapter);
|
builder.registerTypeAdapter(JsonBall.class, jsonObjectAdapter);
|
||||||
builder.registerTypeAdapter(Date.class, adapter);
|
builder.registerTypeAdapter(Date.class, adapter);
|
||||||
builder.registerTypeAdapter(new TypeToken<List<Byte>>() {
|
builder.registerTypeAdapter(new TypeToken<List<Byte>>() {
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (C) 2010 Cloud Conscious, LLC. <info@cloudconscious.com>
|
||||||
|
*
|
||||||
|
* ====================================================================
|
||||||
|
* 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 org.jclouds.json;
|
||||||
|
|
||||||
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
|
import org.jclouds.json.config.GsonModule;
|
||||||
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.inject.Guice;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public class JsonTest {
|
||||||
|
private Json json = Guice.createInjector(new GsonModule()).getInstance(Json.class);
|
||||||
|
|
||||||
|
private static class EnumInside {
|
||||||
|
private static enum Test {
|
||||||
|
FOO, BAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Test enumValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeserializeEnum() {
|
||||||
|
assertEquals(json.fromJson("{enumValue : \"FOO\"}", EnumInside.class).enumValue, EnumInside.Test.FOO);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expectedExceptions = JsonParseException.class)
|
||||||
|
public void testDeserializeEnumWhenBadValue() {
|
||||||
|
assertEquals(json.fromJson("{enumValue : \"s\"}", EnumInside.class).enumValue, EnumInside.Test.FOO);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class EnumInsideWithParser {
|
||||||
|
private static enum Test {
|
||||||
|
FOO, BAR, UNRECOGNIZED;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static Test fromValue(String state) {
|
||||||
|
try {
|
||||||
|
return valueOf(state);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return UNRECOGNIZED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Test enumValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeserializeEnumWithParser() {
|
||||||
|
assertEquals(json.fromJson("{enumValue : \"FOO\"}", EnumInsideWithParser.class).enumValue,
|
||||||
|
EnumInsideWithParser.Test.FOO);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDeserializeEnumWithParserAndBadValue() {
|
||||||
|
assertEquals(json.fromJson("{enumValue : \"sd\"}", EnumInsideWithParser.class).enumValue,
|
||||||
|
EnumInsideWithParser.Test.UNRECOGNIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue