gson-comments/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java

880 lines
29 KiB
Java
Raw Normal View History

2011-07-11 18:46:52 +02:00
/*
* 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.
*/
2011-08-03 02:25:10 +02:00
package com.google.gson.internal.bind;
2011-07-11 18:46:52 +02:00
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Calendar;
2015-11-07 00:41:15 +01:00
import java.util.Currency;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerArray;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSyntaxException;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.annotations.SerializedName;
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;
2011-07-11 18:46:52 +02:00
/**
* Type adapters for basic types.
*/
public final class TypeAdapters {
private TypeAdapters() {
throw new UnsupportedOperationException();
}
2011-07-11 18:46:52 +02:00
@SuppressWarnings("rawtypes")
public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
@Override
public void write(JsonWriter out, Class value) throws IOException {
throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: "
+ value.getName() + ". Forgot to register a type adapter?");
}
@Override
public Class read(JsonReader in) throws IOException {
throw new UnsupportedOperationException(
"Attempted to deserialize a java.lang.Class. Forgot to register a type adapter?");
}
}.nullSafe();
public static final TypeAdapterFactory CLASS_FACTORY = newFactory(Class.class, CLASS);
public static final TypeAdapter<BitSet> BIT_SET = new TypeAdapter<BitSet>() {
@Override public BitSet read(JsonReader in) throws IOException {
BitSet bitset = new BitSet();
2011-12-03 20:46:25 +01:00
in.beginArray();
int i = 0;
2011-12-03 20:46:25 +01:00
JsonToken tokenType = in.peek();
while (tokenType != JsonToken.END_ARRAY) {
boolean set;
switch (tokenType) {
case NUMBER:
2011-12-03 20:46:25 +01:00
set = in.nextInt() != 0;
break;
case BOOLEAN:
2011-12-03 20:46:25 +01:00
set = in.nextBoolean();
break;
case STRING:
2011-12-03 20:46:25 +01:00
String stringValue = in.nextString();
try {
set = Integer.parseInt(stringValue) != 0;
} catch (NumberFormatException e) {
throw new JsonSyntaxException(
"Error: Expecting: bitset number value (1, 0), Found: " + stringValue);
}
break;
default:
throw new JsonSyntaxException("Invalid bitset value type: " + tokenType);
}
if (set) {
bitset.set(i);
}
++i;
2011-12-03 20:46:25 +01:00
tokenType = in.peek();
}
2011-12-03 20:46:25 +01:00
in.endArray();
return bitset;
}
@Override public void write(JsonWriter out, BitSet src) throws IOException {
2011-12-03 20:46:25 +01:00
out.beginArray();
for (int i = 0, length = src.length(); i < length; i++) {
int value = (src.get(i)) ? 1 : 0;
2011-12-03 20:46:25 +01:00
out.value(value);
}
2011-12-03 20:46:25 +01:00
out.endArray();
}
}.nullSafe();
public static final TypeAdapterFactory BIT_SET_FACTORY = newFactory(BitSet.class, BIT_SET);
2011-07-11 18:46:52 +02:00
public static final TypeAdapter<Boolean> BOOLEAN = new TypeAdapter<Boolean>() {
@Override
2011-12-03 20:46:25 +01:00
public Boolean read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
if (peek == JsonToken.NULL) {
2011-12-03 20:46:25 +01:00
in.nextNull();
return null;
} else if (peek == JsonToken.STRING) {
// support strings for compatibility with GSON 1.7
2011-12-03 20:46:25 +01:00
return Boolean.parseBoolean(in.nextString());
}
2011-12-03 20:46:25 +01:00
return in.nextBoolean();
2011-07-11 18:46:52 +02:00
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Boolean value) throws IOException {
out.value(value);
2011-07-11 18:46:52 +02:00
}
};
/**
* Writes a boolean as a string. Useful for map keys, where booleans aren't
* otherwise permitted.
*/
public static final TypeAdapter<Boolean> BOOLEAN_AS_STRING = new TypeAdapter<Boolean>() {
2011-12-03 20:46:25 +01:00
@Override public Boolean read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
return Boolean.valueOf(in.nextString());
}
2011-12-03 20:46:25 +01:00
@Override public void write(JsonWriter out, Boolean value) throws IOException {
out.value(value == null ? "null" : value.toString());
}
};
public static final TypeAdapterFactory BOOLEAN_FACTORY
2011-07-11 18:46:52 +02:00
= newFactory(boolean.class, Boolean.class, BOOLEAN);
2011-09-09 06:39:29 +02:00
public static final TypeAdapter<Number> BYTE = new TypeAdapter<Number>() {
@Override
2011-12-03 20:46:25 +01:00
public Number read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
2011-12-03 20:46:25 +01:00
int intValue = in.nextInt();
return (byte) intValue;
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
2011-07-11 18:46:52 +02:00
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
2011-07-11 18:46:52 +02:00
}
};
public static final TypeAdapterFactory BYTE_FACTORY
2011-09-09 06:39:29 +02:00
= newFactory(byte.class, Byte.class, BYTE);
2011-07-11 18:46:52 +02:00
2011-09-09 06:39:29 +02:00
public static final TypeAdapter<Number> SHORT = new TypeAdapter<Number>() {
@Override
2011-12-03 20:46:25 +01:00
public Number read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
2011-12-03 20:46:25 +01:00
return (short) in.nextInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
}
};
public static final TypeAdapterFactory SHORT_FACTORY
= newFactory(short.class, Short.class, SHORT);
2011-09-09 06:39:29 +02:00
public static final TypeAdapter<Number> INTEGER = new TypeAdapter<Number>() {
@Override
2011-12-03 20:46:25 +01:00
public Number read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
2011-12-03 20:46:25 +01:00
return in.nextInt();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
2011-07-11 18:46:52 +02:00
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
2011-07-11 18:46:52 +02:00
}
};
public static final TypeAdapterFactory INTEGER_FACTORY
2011-09-09 06:39:29 +02:00
= newFactory(int.class, Integer.class, INTEGER);
2011-07-11 18:46:52 +02:00
public static final TypeAdapter<AtomicInteger> ATOMIC_INTEGER = new TypeAdapter<AtomicInteger>() {
@Override public AtomicInteger read(JsonReader in) throws IOException {
try {
return new AtomicInteger(in.nextInt());
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override public void write(JsonWriter out, AtomicInteger value) throws IOException {
out.value(value.get());
}
}.nullSafe();
2015-11-05 23:03:51 +01:00
public static final TypeAdapterFactory ATOMIC_INTEGER_FACTORY =
newFactory(AtomicInteger.class, TypeAdapters.ATOMIC_INTEGER);
public static final TypeAdapter<AtomicBoolean> ATOMIC_BOOLEAN = new TypeAdapter<AtomicBoolean>() {
@Override public AtomicBoolean read(JsonReader in) throws IOException {
return new AtomicBoolean(in.nextBoolean());
}
@Override public void write(JsonWriter out, AtomicBoolean value) throws IOException {
out.value(value.get());
}
}.nullSafe();
2015-11-05 23:03:51 +01:00
public static final TypeAdapterFactory ATOMIC_BOOLEAN_FACTORY =
newFactory(AtomicBoolean.class, TypeAdapters.ATOMIC_BOOLEAN);
public static final TypeAdapter<AtomicIntegerArray> ATOMIC_INTEGER_ARRAY = new TypeAdapter<AtomicIntegerArray>() {
@Override public AtomicIntegerArray read(JsonReader in) throws IOException {
List<Integer> list = new ArrayList<Integer>();
in.beginArray();
while (in.hasNext()) {
try {
int integer = in.nextInt();
list.add(integer);
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
in.endArray();
int length = list.size();
AtomicIntegerArray array = new AtomicIntegerArray(length);
for (int i = 0; i < length; ++i) {
array.set(i, list.get(i));
}
return array;
}
@Override public void write(JsonWriter out, AtomicIntegerArray value) throws IOException {
out.beginArray();
for (int i = 0, length = value.length(); i < length; i++) {
out.value(value.get(i));
}
out.endArray();
}
}.nullSafe();
2015-11-05 23:03:51 +01:00
public static final TypeAdapterFactory ATOMIC_INTEGER_ARRAY_FACTORY =
newFactory(AtomicIntegerArray.class, TypeAdapters.ATOMIC_INTEGER_ARRAY);
2011-09-09 06:39:29 +02:00
public static final TypeAdapter<Number> LONG = new TypeAdapter<Number>() {
@Override
2011-12-03 20:46:25 +01:00
public Number read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
2011-12-03 20:46:25 +01:00
return in.nextLong();
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
2011-07-11 18:46:52 +02:00
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
2011-07-11 18:46:52 +02:00
}
};
2011-09-09 06:39:29 +02:00
public static final TypeAdapter<Number> FLOAT = new TypeAdapter<Number>() {
@Override
2011-12-03 20:46:25 +01:00
public Number read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
return (float) in.nextDouble();
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
}
};
2011-09-09 06:39:29 +02:00
public static final TypeAdapter<Number> DOUBLE = new TypeAdapter<Number>() {
@Override
2011-12-03 20:46:25 +01:00
public Number read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
return in.nextDouble();
2011-09-09 06:39:29 +02:00
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Number value) throws IOException {
out.value(value);
2011-09-09 06:39:29 +02:00
}
};
public static final TypeAdapter<Character> CHARACTER = new TypeAdapter<Character>() {
@Override
2011-12-03 20:46:25 +01:00
public Character read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-16 20:10:54 +01:00
String str = in.nextString();
if (str.length() != 1) {
throw new JsonSyntaxException("Expecting character, got: " + str);
}
return str.charAt(0);
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Character value) throws IOException {
out.value(value == null ? null : String.valueOf(value));
}
};
public static final TypeAdapterFactory CHARACTER_FACTORY
= newFactory(char.class, Character.class, CHARACTER);
2011-07-11 18:46:52 +02:00
public static final TypeAdapter<String> STRING = new TypeAdapter<String>() {
@Override
2011-12-03 20:46:25 +01:00
public String read(JsonReader in) throws IOException {
JsonToken peek = in.peek();
Down to 22 failing tests. Consolidated all of the different code paths that we use to construct instances. We now have an ObjectConstructor class that knows what type it constructs; this means that we don't need to ever do reflection to lookup a constructor at construction time. Cleaned up some buggy type adapters, particularly around handling of null. Removed dead code for object graph navigation. Moved some classes into 'internal' so they are visible to the 'bind' subpackage. Turned some TypeAdapterFactory/TypeAdapter pairs inside out so that the TypeAdapter is now the inner class. This is necessary so that the factories can take parameters. Added an API to request the 'next' type adapter for a type. This allows type adapters to compose other type adapters. We're using this in two places: - where the user has excluded a type from serialization but not deserialization, we need to use the "default" deserialization but interpose null on serialization. We create a type adapter that delegates for one and returns null for the other. - similarly when a DOM type serializer is registered but no deserializer, or vice versa. This is the biggest change to the MiniGson core. For backwards compatibility, return null for the empty string. Simplify JsonSerializationContext/JsonDeserializationContext to simply call through to GSON. SerializeDefault is currently unsupported. More useful error messages when calling getAsBoolean on a JsonNull. Remove currently unused MemoryRefStack. We might need this back again, though wiring it back in will be much more difficult because we don't interject ourselves between the users' various type adapters.
2011-09-11 09:04:56 +02:00
if (peek == JsonToken.NULL) {
2011-12-03 20:46:25 +01:00
in.nextNull();
2011-09-09 05:40:39 +02:00
return null;
}
Down to 22 failing tests. Consolidated all of the different code paths that we use to construct instances. We now have an ObjectConstructor class that knows what type it constructs; this means that we don't need to ever do reflection to lookup a constructor at construction time. Cleaned up some buggy type adapters, particularly around handling of null. Removed dead code for object graph navigation. Moved some classes into 'internal' so they are visible to the 'bind' subpackage. Turned some TypeAdapterFactory/TypeAdapter pairs inside out so that the TypeAdapter is now the inner class. This is necessary so that the factories can take parameters. Added an API to request the 'next' type adapter for a type. This allows type adapters to compose other type adapters. We're using this in two places: - where the user has excluded a type from serialization but not deserialization, we need to use the "default" deserialization but interpose null on serialization. We create a type adapter that delegates for one and returns null for the other. - similarly when a DOM type serializer is registered but no deserializer, or vice versa. This is the biggest change to the MiniGson core. For backwards compatibility, return null for the empty string. Simplify JsonSerializationContext/JsonDeserializationContext to simply call through to GSON. SerializeDefault is currently unsupported. More useful error messages when calling getAsBoolean on a JsonNull. Remove currently unused MemoryRefStack. We might need this back again, though wiring it back in will be much more difficult because we don't interject ourselves between the users' various type adapters.
2011-09-11 09:04:56 +02:00
/* coerce booleans to strings for backwards compatibility */
if (peek == JsonToken.BOOLEAN) {
2011-12-03 20:46:25 +01:00
return Boolean.toString(in.nextBoolean());
Down to 22 failing tests. Consolidated all of the different code paths that we use to construct instances. We now have an ObjectConstructor class that knows what type it constructs; this means that we don't need to ever do reflection to lookup a constructor at construction time. Cleaned up some buggy type adapters, particularly around handling of null. Removed dead code for object graph navigation. Moved some classes into 'internal' so they are visible to the 'bind' subpackage. Turned some TypeAdapterFactory/TypeAdapter pairs inside out so that the TypeAdapter is now the inner class. This is necessary so that the factories can take parameters. Added an API to request the 'next' type adapter for a type. This allows type adapters to compose other type adapters. We're using this in two places: - where the user has excluded a type from serialization but not deserialization, we need to use the "default" deserialization but interpose null on serialization. We create a type adapter that delegates for one and returns null for the other. - similarly when a DOM type serializer is registered but no deserializer, or vice versa. This is the biggest change to the MiniGson core. For backwards compatibility, return null for the empty string. Simplify JsonSerializationContext/JsonDeserializationContext to simply call through to GSON. SerializeDefault is currently unsupported. More useful error messages when calling getAsBoolean on a JsonNull. Remove currently unused MemoryRefStack. We might need this back again, though wiring it back in will be much more difficult because we don't interject ourselves between the users' various type adapters.
2011-09-11 09:04:56 +02:00
}
2011-12-03 20:46:25 +01:00
return in.nextString();
2011-07-11 18:46:52 +02:00
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, String value) throws IOException {
out.value(value);
2011-07-11 18:46:52 +02:00
}
};
2020-05-09 17:37:21 +02:00
public static final TypeAdapter<BigDecimal> BIG_DECIMAL = new TypeAdapter<BigDecimal>() {
@Override public BigDecimal read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
return new BigDecimal(in.nextString());
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override public void write(JsonWriter out, BigDecimal value) throws IOException {
out.value(value);
}
};
2020-05-09 17:37:21 +02:00
public static final TypeAdapter<BigInteger> BIG_INTEGER = new TypeAdapter<BigInteger>() {
@Override public BigInteger read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
return new BigInteger(in.nextString());
} catch (NumberFormatException e) {
throw new JsonSyntaxException(e);
}
}
@Override public void write(JsonWriter out, BigInteger value) throws IOException {
out.value(value);
}
};
2011-07-11 18:46:52 +02:00
public static final TypeAdapterFactory STRING_FACTORY = newFactory(String.class, STRING);
2011-07-11 18:46:52 +02:00
public static final TypeAdapter<StringBuilder> STRING_BUILDER = new TypeAdapter<StringBuilder>() {
@Override
2011-12-03 20:46:25 +01:00
public StringBuilder read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
return new StringBuilder(in.nextString());
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, StringBuilder value) throws IOException {
out.value(value == null ? null : value.toString());
}
};
public static final TypeAdapterFactory STRING_BUILDER_FACTORY =
newFactory(StringBuilder.class, STRING_BUILDER);
public static final TypeAdapter<StringBuffer> STRING_BUFFER = new TypeAdapter<StringBuffer>() {
@Override
2011-12-03 20:46:25 +01:00
public StringBuffer read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
return new StringBuffer(in.nextString());
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, StringBuffer value) throws IOException {
out.value(value == null ? null : value.toString());
}
};
public static final TypeAdapterFactory STRING_BUFFER_FACTORY =
newFactory(StringBuffer.class, STRING_BUFFER);
public static final TypeAdapter<URL> URL = new TypeAdapter<URL>() {
@Override
2011-12-03 20:46:25 +01:00
public URL read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
String nextString = in.nextString();
return "null".equals(nextString) ? null : new URL(nextString);
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, URL value) throws IOException {
out.value(value == null ? null : value.toExternalForm());
}
};
public static final TypeAdapterFactory URL_FACTORY = newFactory(URL.class, URL);
public static final TypeAdapter<URI> URI = new TypeAdapter<URI>() {
@Override
2011-12-03 20:46:25 +01:00
public URI read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
try {
2011-12-03 20:46:25 +01:00
String nextString = in.nextString();
return "null".equals(nextString) ? null : new URI(nextString);
} catch (URISyntaxException e) {
throw new JsonIOException(e);
}
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, URI value) throws IOException {
out.value(value == null ? null : value.toASCIIString());
}
};
public static final TypeAdapterFactory URI_FACTORY = newFactory(URI.class, URI);
public static final TypeAdapter<InetAddress> INET_ADDRESS = new TypeAdapter<InetAddress>() {
@Override
2011-12-03 20:46:25 +01:00
public InetAddress read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
// regrettably, this should have included both the host name and the host address
2011-12-03 20:46:25 +01:00
return InetAddress.getByName(in.nextString());
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, InetAddress value) throws IOException {
out.value(value == null ? null : value.getHostAddress());
}
};
public static final TypeAdapterFactory INET_ADDRESS_FACTORY =
newTypeHierarchyFactory(InetAddress.class, INET_ADDRESS);
public static final TypeAdapter<UUID> UUID = new TypeAdapter<UUID>() {
@Override
2011-12-03 20:46:25 +01:00
public UUID read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
return java.util.UUID.fromString(in.nextString());
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, UUID value) throws IOException {
out.value(value == null ? null : value.toString());
}
};
public static final TypeAdapterFactory UUID_FACTORY = newFactory(UUID.class, UUID);
2015-11-07 00:41:15 +01:00
public static final TypeAdapter<Currency> CURRENCY = new TypeAdapter<Currency>() {
@Override
public Currency read(JsonReader in) throws IOException {
return Currency.getInstance(in.nextString());
}
@Override
public void write(JsonWriter out, Currency value) throws IOException {
out.value(value.getCurrencyCode());
}
}.nullSafe();
public static final TypeAdapterFactory CURRENCY_FACTORY = newFactory(Currency.class, CURRENCY);
public static final TypeAdapter<Calendar> CALENDAR = new TypeAdapter<Calendar>() {
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";
@Override
2011-12-03 20:46:25 +01:00
public Calendar read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
in.beginObject();
int year = 0;
int month = 0;
int dayOfMonth = 0;
int hourOfDay = 0;
int minute = 0;
int second = 0;
2011-12-03 20:46:25 +01:00
while (in.peek() != JsonToken.END_OBJECT) {
String name = in.nextName();
int value = in.nextInt();
if (YEAR.equals(name)) {
year = value;
} else if (MONTH.equals(name)) {
month = value;
} else if (DAY_OF_MONTH.equals(name)) {
dayOfMonth = value;
} else if (HOUR_OF_DAY.equals(name)) {
hourOfDay = value;
} else if (MINUTE.equals(name)) {
minute = value;
} else if (SECOND.equals(name)) {
second = value;
}
}
2011-12-03 20:46:25 +01:00
in.endObject();
return new GregorianCalendar(year, month, dayOfMonth, hourOfDay, minute, second);
}
@Override
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Calendar value) throws IOException {
if (value == null) {
2011-12-03 20:46:25 +01:00
out.nullValue();
return;
}
2011-12-03 20:46:25 +01:00
out.beginObject();
out.name(YEAR);
out.value(value.get(Calendar.YEAR));
out.name(MONTH);
out.value(value.get(Calendar.MONTH));
out.name(DAY_OF_MONTH);
out.value(value.get(Calendar.DAY_OF_MONTH));
out.name(HOUR_OF_DAY);
out.value(value.get(Calendar.HOUR_OF_DAY));
out.name(MINUTE);
out.value(value.get(Calendar.MINUTE));
out.name(SECOND);
out.value(value.get(Calendar.SECOND));
out.endObject();
}
};
public static final TypeAdapterFactory CALENDAR_FACTORY =
newFactoryForMultipleTypes(Calendar.class, GregorianCalendar.class, CALENDAR);
public static final TypeAdapter<Locale> LOCALE = new TypeAdapter<Locale>() {
@Override
2011-12-03 20:46:25 +01:00
public Locale read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
2011-12-03 20:46:25 +01:00
String locale = in.nextString();
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
2011-12-03 20:46:25 +01:00
public void write(JsonWriter out, Locale value) throws IOException {
out.value(value == null ? null : value.toString());
}
};
public static final TypeAdapterFactory LOCALE_FACTORY = newFactory(Locale.class, LOCALE);
public static final TypeAdapter<JsonElement> JSON_ELEMENT = new TypeAdapter<JsonElement>() {
2011-12-03 20:46:25 +01:00
@Override public JsonElement read(JsonReader in) throws IOException {
if (in instanceof JsonTreeReader) {
return ((JsonTreeReader) in).nextJsonElement();
}
2011-12-03 20:46:25 +01:00
switch (in.peek()) {
case STRING:
2011-12-03 20:46:25 +01:00
return new JsonPrimitive(in.nextString());
case NUMBER:
2011-12-03 20:46:25 +01:00
String number = in.nextString();
return new JsonPrimitive(new LazilyParsedNumber(number));
case BOOLEAN:
2011-12-03 20:46:25 +01:00
return new JsonPrimitive(in.nextBoolean());
case NULL:
2011-12-03 20:46:25 +01:00
in.nextNull();
return JsonNull.INSTANCE;
case BEGIN_ARRAY:
JsonArray array = new JsonArray();
2011-12-03 20:46:25 +01:00
in.beginArray();
while (in.hasNext()) {
array.add(read(in));
}
2011-12-03 20:46:25 +01:00
in.endArray();
return array;
case BEGIN_OBJECT:
JsonObject object = new JsonObject();
2011-12-03 20:46:25 +01:00
in.beginObject();
while (in.hasNext()) {
object.add(in.nextName(), read(in));
}
2011-12-03 20:46:25 +01:00
in.endObject();
return object;
case END_DOCUMENT:
case NAME:
case END_OBJECT:
case END_ARRAY:
default:
throw new IllegalArgumentException();
}
}
2011-12-03 20:46:25 +01:00
@Override public void write(JsonWriter out, JsonElement value) throws IOException {
if (value == null || value.isJsonNull()) {
2011-12-03 20:46:25 +01:00
out.nullValue();
} else if (value.isJsonPrimitive()) {
JsonPrimitive primitive = value.getAsJsonPrimitive();
if (primitive.isNumber()) {
2011-12-03 20:46:25 +01:00
out.value(primitive.getAsNumber());
} else if (primitive.isBoolean()) {
2011-12-03 20:46:25 +01:00
out.value(primitive.getAsBoolean());
} else {
2011-12-03 20:46:25 +01:00
out.value(primitive.getAsString());
}
} else if (value.isJsonArray()) {
2011-12-03 20:46:25 +01:00
out.beginArray();
for (JsonElement e : value.getAsJsonArray()) {
2011-12-03 20:46:25 +01:00
write(out, e);
}
2011-12-03 20:46:25 +01:00
out.endArray();
} else if (value.isJsonObject()) {
2011-12-03 20:46:25 +01:00
out.beginObject();
for (Map.Entry<String, JsonElement> e : value.getAsJsonObject().entrySet()) {
2011-12-03 20:46:25 +01:00
out.name(e.getKey());
write(out, e.getValue());
}
2011-12-03 20:46:25 +01:00
out.endObject();
} else {
throw new IllegalArgumentException("Couldn't write " + value.getClass());
}
}
};
public static final TypeAdapterFactory JSON_ELEMENT_FACTORY
= newTypeHierarchyFactory(JsonElement.class, JSON_ELEMENT);
private static final class EnumTypeAdapter<T extends Enum<T>> extends TypeAdapter<T> {
private final Map<String, T> nameToConstant = new HashMap<String, T>();
private final Map<T, String> constantToName = new HashMap<T, String>();
public EnumTypeAdapter(Class<T> classOfT) {
try {
for (final Field field : classOfT.getDeclaredFields()) {
if (!field.isEnumConstant()) {
continue;
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override public Void run() {
field.setAccessible(true);
return null;
}
});
2020-09-12 06:40:09 +02:00
@SuppressWarnings("unchecked")
T constant = (T)(field.get(null));
String name = constant.name();
SerializedName annotation = field.getAnnotation(SerializedName.class);
if (annotation != null) {
name = annotation.value();
for (String alternate : annotation.alternate()) {
nameToConstant.put(alternate, constant);
}
}
nameToConstant.put(name, constant);
constantToName.put(constant, name);
}
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
}
@Override public T read(JsonReader in) throws IOException {
2011-12-03 20:46:25 +01:00
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return nameToConstant.get(in.nextString());
}
@Override public void write(JsonWriter out, T value) throws IOException {
out.value(value == null ? null : constantToName.get(value));
}
}
public static final TypeAdapterFactory ENUM_FACTORY = new TypeAdapterFactory() {
@SuppressWarnings({"rawtypes", "unchecked"})
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
if (!Enum.class.isAssignableFrom(rawType) || rawType == Enum.class) {
return null;
}
if (!rawType.isEnum()) {
rawType = rawType.getSuperclass(); // handle anonymous subclasses
}
return (TypeAdapter<T>) new EnumTypeAdapter(rawType);
}
};
public static <TT> TypeAdapterFactory newFactory(
final TypeToken<TT> type, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapterFactory() {
2011-07-11 18:46:52 +02:00
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
2011-07-11 18:46:52 +02:00
return typeToken.equals(type) ? (TypeAdapter<T>) typeAdapter : null;
}
};
}
public static <TT> TypeAdapterFactory newFactory(
final Class<TT> type, final TypeAdapter<TT> typeAdapter) {
return new TypeAdapterFactory() {
2011-07-11 18:46:52 +02:00
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
2011-07-11 18:46:52 +02:00
return typeToken.getRawType() == type ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {
return "Factory[type=" + type.getName() + ",adapter=" + typeAdapter + "]";
}
2011-07-11 18:46:52 +02:00
};
}
public static <TT> TypeAdapterFactory newFactory(
final Class<TT> unboxed, final Class<TT> boxed, final TypeAdapter<? super TT> typeAdapter) {
return new TypeAdapterFactory() {
2011-07-11 18:46:52 +02:00
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
2011-07-11 18:46:52 +02:00
Class<? super T> rawType = typeToken.getRawType();
return (rawType == unboxed || rawType == boxed) ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {
return "Factory[type=" + boxed.getName()
+ "+" + unboxed.getName() + ",adapter=" + typeAdapter + "]";
}
2011-07-11 18:46:52 +02:00
};
}
public static <TT> TypeAdapterFactory newFactoryForMultipleTypes(final Class<TT> base,
final Class<? extends TT> sub, final TypeAdapter<? super TT> typeAdapter) {
return new TypeAdapterFactory() {
@SuppressWarnings("unchecked") // we use a runtime check to make sure the 'T's equal
@Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
Class<? super T> rawType = typeToken.getRawType();
return (rawType == base || rawType == sub) ? (TypeAdapter<T>) typeAdapter : null;
}
@Override public String toString() {
return "Factory[type=" + base.getName()
+ "+" + sub.getName() + ",adapter=" + typeAdapter + "]";
}
};
}
/**
* Returns a factory for all subtypes of {@code typeAdapter}. We do a runtime check to confirm
* that the deserialized type matches the type requested.
*/
public static <T1> TypeAdapterFactory newTypeHierarchyFactory(
final Class<T1> clazz, final TypeAdapter<T1> typeAdapter) {
return new TypeAdapterFactory() {
@SuppressWarnings("unchecked")
@Override public <T2> TypeAdapter<T2> create(Gson gson, TypeToken<T2> typeToken) {
final Class<? super T2> requestedType = typeToken.getRawType();
if (!clazz.isAssignableFrom(requestedType)) {
return null;
}
return (TypeAdapter<T2>) new TypeAdapter<T1>() {
@Override public void write(JsonWriter out, T1 value) throws IOException {
typeAdapter.write(out, value);
}
@Override public T1 read(JsonReader in) throws IOException {
T1 result = typeAdapter.read(in);
if (result != null && !requestedType.isInstance(result)) {
throw new JsonSyntaxException("Expected a " + requestedType.getName()
+ " but was " + result.getClass().getName());
}
return result;
}
};
2011-07-11 18:46:52 +02:00
}
@Override public String toString() {
return "Factory[typeHierarchy=" + clazz.getName() + ",adapter=" + typeAdapter + "]";
}
2011-07-11 18:46:52 +02:00
};
}
}