Added support for JsonSerializer/JsonDeserializer for JsonAdapter annotation.
JsonAdapter is cached per the type of the JsonAdapter class. Added a test to ensure JsonAdapter works on fields of parameterized types Keep track of registered JsonAdapters and JsonAdapterFactorys in ThreadLocal.
This commit is contained in:
parent
854760e6c7
commit
45511fdd15
@ -134,6 +134,7 @@ public final class Gson {
|
|||||||
private final boolean generateNonExecutableJson;
|
private final boolean generateNonExecutableJson;
|
||||||
private final boolean prettyPrinting;
|
private final boolean prettyPrinting;
|
||||||
private final boolean lenient;
|
private final boolean lenient;
|
||||||
|
private JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a Gson object with default configuration. The default configuration has the
|
* Constructs a Gson object with default configuration. The default configuration has the
|
||||||
@ -245,10 +246,11 @@ public final class Gson {
|
|||||||
// type adapters for composite and user-defined types
|
// type adapters for composite and user-defined types
|
||||||
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
||||||
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
|
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
|
||||||
factories.add(new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor));
|
this.jsonAdapterFactory = new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor);
|
||||||
|
factories.add(jsonAdapterFactory);
|
||||||
factories.add(TypeAdapters.ENUM_FACTORY);
|
factories.add(TypeAdapters.ENUM_FACTORY);
|
||||||
factories.add(new ReflectiveTypeAdapterFactory(
|
factories.add(new ReflectiveTypeAdapterFactory(
|
||||||
constructorConstructor, fieldNamingStrategy, excluder));
|
constructorConstructor, fieldNamingStrategy, excluder, jsonAdapterFactory));
|
||||||
|
|
||||||
this.factories = Collections.unmodifiableList(factories);
|
this.factories = Collections.unmodifiableList(factories);
|
||||||
}
|
}
|
||||||
@ -486,26 +488,26 @@ public final class Gson {
|
|||||||
* @since 2.2
|
* @since 2.2
|
||||||
*/
|
*/
|
||||||
public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) {
|
public <T> TypeAdapter<T> getDelegateAdapter(TypeAdapterFactory skipPast, TypeToken<T> type) {
|
||||||
boolean skipPastFound = false;
|
// If the specified skipPast factory is not registered, ignore it.
|
||||||
// Skip past if and only if the specified factory is present in the factories.
|
boolean skipPastFound = skipPast == null
|
||||||
// This is useful because the factories created through JsonAdapter annotations are not
|
|| (!factories.contains(skipPast) && jsonAdapterFactory.getDelegateAdapterFactory(type) == null);
|
||||||
// registered in this list.
|
|
||||||
if (!factories.contains(skipPast)) skipPastFound = true;
|
|
||||||
|
|
||||||
for (TypeAdapterFactory factory : factories) {
|
for (TypeAdapterFactory factory : factories) {
|
||||||
if (!skipPastFound) {
|
if (!skipPastFound) {
|
||||||
if (factory == skipPast) {
|
skipPastFound = factory == skipPast;
|
||||||
skipPastFound = true;
|
if (!skipPastFound && factory instanceof JsonAdapterAnnotationTypeAdapterFactory) {
|
||||||
|
// Also check if there is a registered JsonAdapter for it
|
||||||
|
factory = ((JsonAdapterAnnotationTypeAdapterFactory)factory).getDelegateAdapterFactory(type);
|
||||||
|
skipPastFound = factory == skipPast;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeAdapter<T> candidate = factory.create(this, type);
|
TypeAdapter<T> candidate = factory.create(this, type);
|
||||||
if (candidate != null) {
|
if (candidate != null) {
|
||||||
return candidate;
|
return candidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException("GSON cannot serialize " + type);
|
throw new IllegalArgumentException("GSON cannot serialize or deserialize " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +16,12 @@
|
|||||||
|
|
||||||
package com.google.gson.internal.bind;
|
package com.google.gson.internal.bind;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
import com.google.gson.TypeAdapterFactory;
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import com.google.gson.annotations.JsonAdapter;
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
@ -31,6 +36,21 @@ import com.google.gson.reflect.TypeToken;
|
|||||||
*/
|
*/
|
||||||
public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
|
public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private final ThreadLocal<Map<Class, TypeAdapter>> activeJsonAdapterClasses = new ThreadLocal<Map<Class, TypeAdapter>>() {
|
||||||
|
@Override protected Map<Class, TypeAdapter> initialValue() {
|
||||||
|
// No need for a thread-safe map since we are using it in a single thread
|
||||||
|
return new HashMap<Class, TypeAdapter>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private final ThreadLocal<Map<Class, TypeAdapterFactory>> activeJsonAdapterFactories = new ThreadLocal<Map<Class, TypeAdapterFactory>>() {
|
||||||
|
@Override protected Map<Class, TypeAdapterFactory> initialValue() {
|
||||||
|
// No need for a thread-safe map since we are using it in a single thread
|
||||||
|
return new HashMap<Class, TypeAdapterFactory>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final ConstructorConstructor constructorConstructor;
|
private final ConstructorConstructor constructorConstructor;
|
||||||
|
|
||||||
public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
|
public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
|
||||||
@ -40,33 +60,86 @@ public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapte
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
|
||||||
JsonAdapter annotation = targetType.getRawType().getAnnotation(JsonAdapter.class);
|
Class<? super T> rawType = targetType.getRawType();
|
||||||
|
JsonAdapter annotation = rawType.getAnnotation(JsonAdapter.class);
|
||||||
if (annotation == null) {
|
if (annotation == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
|
return (TypeAdapter<T>) getTypeAdapter(constructorConstructor, gson, targetType, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked") // Casts guarded by conditionals.
|
public <T> TypeAdapter<T> getDelegateAdapter(Gson gson, TypeAdapterFactory skipPast, TypeToken<T> targetType) {
|
||||||
static TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
|
TypeAdapterFactory factory = getDelegateAdapterFactory(targetType);
|
||||||
TypeToken<?> fieldType, JsonAdapter annotation) {
|
if (factory == skipPast) factory = null;
|
||||||
|
return factory == null ? null: factory.create(gson, targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> TypeAdapterFactory getDelegateAdapterFactory(TypeToken<T> targetType) {
|
||||||
|
Class<?> annotatedClass = targetType.getRawType();
|
||||||
|
JsonAdapter annotation = annotatedClass.getAnnotation(JsonAdapter.class);
|
||||||
|
if (annotation == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return getTypeAdapterFactory(annotation, constructorConstructor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals.
|
||||||
|
TypeAdapter<?> getTypeAdapter(ConstructorConstructor constructorConstructor, Gson gson,
|
||||||
|
TypeToken<?> type, JsonAdapter annotation) {
|
||||||
Class<?> value = annotation.value();
|
Class<?> value = annotation.value();
|
||||||
|
boolean isTypeAdapter = TypeAdapter.class.isAssignableFrom(value);
|
||||||
|
boolean isJsonSerializer = JsonSerializer.class.isAssignableFrom(value);
|
||||||
|
boolean isJsonDeserializer = JsonDeserializer.class.isAssignableFrom(value);
|
||||||
|
|
||||||
TypeAdapter<?> typeAdapter;
|
TypeAdapter<?> typeAdapter;
|
||||||
if (TypeAdapter.class.isAssignableFrom(value)) {
|
if (isTypeAdapter || isJsonSerializer || isJsonDeserializer) {
|
||||||
Class<TypeAdapter<?>> typeAdapterClass = (Class<TypeAdapter<?>>) value;
|
Map<Class, TypeAdapter> adapters = activeJsonAdapterClasses.get();
|
||||||
typeAdapter = constructorConstructor.get(TypeToken.get(typeAdapterClass)).construct();
|
typeAdapter = adapters.get(value);
|
||||||
|
if (typeAdapter == null) {
|
||||||
|
if (isTypeAdapter) {
|
||||||
|
Class<TypeAdapter<?>> typeAdapterClass = (Class<TypeAdapter<?>>) value;
|
||||||
|
typeAdapter = constructorConstructor.get(TypeToken.get(typeAdapterClass)).construct();
|
||||||
|
} else if (isJsonSerializer || isJsonDeserializer) {
|
||||||
|
JsonSerializer serializer = null;
|
||||||
|
if (isJsonSerializer) {
|
||||||
|
Class<JsonSerializer<?>> serializerClass = (Class<JsonSerializer<?>>) value;
|
||||||
|
serializer = constructorConstructor.get(TypeToken.get(serializerClass)).construct();
|
||||||
|
}
|
||||||
|
JsonDeserializer deserializer = null;
|
||||||
|
if (isJsonDeserializer) {
|
||||||
|
Class<JsonDeserializer<?>> deserializerClass = (Class<JsonDeserializer<?>>) value;
|
||||||
|
deserializer = constructorConstructor.get(TypeToken.get(deserializerClass)).construct();
|
||||||
|
}
|
||||||
|
typeAdapter = new TreeTypeAdapter(serializer, deserializer, gson, type, null);
|
||||||
|
}
|
||||||
|
adapters.put(value, typeAdapter);
|
||||||
|
}
|
||||||
} else if (TypeAdapterFactory.class.isAssignableFrom(value)) {
|
} else if (TypeAdapterFactory.class.isAssignableFrom(value)) {
|
||||||
Class<TypeAdapterFactory> typeAdapterFactory = (Class<TypeAdapterFactory>) value;
|
TypeAdapterFactory factory = getTypeAdapterFactory(annotation, constructorConstructor);
|
||||||
typeAdapter = constructorConstructor.get(TypeToken.get(typeAdapterFactory))
|
typeAdapter = factory == null ? null : factory.create(gson, type);
|
||||||
.construct()
|
|
||||||
.create(gson, fieldType);
|
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"@JsonAdapter value must be TypeAdapter or TypeAdapterFactory reference.");
|
"@JsonAdapter value must be TypeAdapter, TypeAdapterFactory, JsonSerializer or JsonDeserializer reference.");
|
||||||
}
|
}
|
||||||
if (typeAdapter != null) {
|
if (typeAdapter != null) {
|
||||||
typeAdapter = typeAdapter.nullSafe();
|
typeAdapter = typeAdapter.nullSafe();
|
||||||
}
|
}
|
||||||
return typeAdapter;
|
return typeAdapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" }) // Casts guarded by conditionals.
|
||||||
|
TypeAdapterFactory getTypeAdapterFactory(JsonAdapter annotation, ConstructorConstructor constructorConstructor) {
|
||||||
|
Class<?> value = annotation.value();
|
||||||
|
if (!TypeAdapterFactory.class.isAssignableFrom(value)) return null;
|
||||||
|
Map<Class, TypeAdapterFactory> adapterFactories = activeJsonAdapterFactories.get();
|
||||||
|
TypeAdapterFactory factory = adapterFactories.get(value);
|
||||||
|
if (factory == null) {
|
||||||
|
Class<TypeAdapterFactory> typeAdapterFactoryClass = (Class<TypeAdapterFactory>) value;
|
||||||
|
factory = constructorConstructor.get(TypeToken.get(typeAdapterFactoryClass))
|
||||||
|
.construct();
|
||||||
|
adapterFactories.put(value, factory);
|
||||||
|
}
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,6 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory.getTypeAdapter;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type adapter that reflects over the fields and methods of a class.
|
* Type adapter that reflects over the fields and methods of a class.
|
||||||
*/
|
*/
|
||||||
@ -51,12 +49,15 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
private final ConstructorConstructor constructorConstructor;
|
private final ConstructorConstructor constructorConstructor;
|
||||||
private final FieldNamingStrategy fieldNamingPolicy;
|
private final FieldNamingStrategy fieldNamingPolicy;
|
||||||
private final Excluder excluder;
|
private final Excluder excluder;
|
||||||
|
private final JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory;
|
||||||
|
|
||||||
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
|
public ReflectiveTypeAdapterFactory(ConstructorConstructor constructorConstructor,
|
||||||
FieldNamingStrategy fieldNamingPolicy, Excluder excluder) {
|
FieldNamingStrategy fieldNamingPolicy, Excluder excluder,
|
||||||
|
JsonAdapterAnnotationTypeAdapterFactory jsonAdapterFactory) {
|
||||||
this.constructorConstructor = constructorConstructor;
|
this.constructorConstructor = constructorConstructor;
|
||||||
this.fieldNamingPolicy = fieldNamingPolicy;
|
this.fieldNamingPolicy = fieldNamingPolicy;
|
||||||
this.excluder = excluder;
|
this.excluder = excluder;
|
||||||
|
this.jsonAdapterFactory = jsonAdapterFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean excludeField(Field f, boolean serialize) {
|
public boolean excludeField(Field f, boolean serialize) {
|
||||||
@ -108,7 +109,8 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
|||||||
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
||||||
TypeAdapter<?> mapped = null;
|
TypeAdapter<?> mapped = null;
|
||||||
if (annotation != null) {
|
if (annotation != null) {
|
||||||
mapped = getTypeAdapter(constructorConstructor, context, fieldType, annotation);
|
mapped = jsonAdapterFactory.getTypeAdapter(
|
||||||
|
constructorConstructor, context, fieldType, annotation);
|
||||||
}
|
}
|
||||||
final boolean jsonAdapterPresent = mapped != null;
|
final boolean jsonAdapterPresent = mapped != null;
|
||||||
if (mapped == null) mapped = context.getAdapter(fieldType);
|
if (mapped == null) mapped = context.getAdapter(fieldType);
|
||||||
|
@ -16,6 +16,10 @@
|
|||||||
|
|
||||||
package com.google.gson.functional;
|
package com.google.gson.functional;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.TypeAdapter;
|
import com.google.gson.TypeAdapter;
|
||||||
@ -24,7 +28,7 @@ import com.google.gson.annotations.JsonAdapter;
|
|||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
import java.io.IOException;
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -268,4 +272,35 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase {
|
|||||||
+ " annotated with @JsonAdapter(LongToStringTypeAdapterFactory.class)");
|
+ " annotated with @JsonAdapter(LongToStringTypeAdapterFactory.class)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testFieldAnnotationWorksForParameterizedType() {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
String json = gson.toJson(new Gizmo2(Arrays.asList(new Part("Part"))));
|
||||||
|
assertEquals("{\"part\":\"GizmoPartTypeAdapterFactory\"}", json);
|
||||||
|
Gizmo2 computer = gson.fromJson("{'part':'Part'}", Gizmo2.class);
|
||||||
|
assertEquals("GizmoPartTypeAdapterFactory", computer.part.get(0).name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Gizmo2 {
|
||||||
|
@JsonAdapter(Gizmo2PartTypeAdapterFactory.class)
|
||||||
|
List<Part> part;
|
||||||
|
Gizmo2(List<Part> part) {
|
||||||
|
this.part = part;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Gizmo2PartTypeAdapterFactory implements TypeAdapterFactory {
|
||||||
|
@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
|
||||||
|
return new TypeAdapter<T>() {
|
||||||
|
@Override public void write(JsonWriter out, T value) throws IOException {
|
||||||
|
out.value("GizmoPartTypeAdapterFactory");
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override public T read(JsonReader in) throws IOException {
|
||||||
|
in.nextString();
|
||||||
|
return (T) Arrays.asList(new Part("GizmoPartTypeAdapterFactory"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* 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.functional;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional tests for the {@link JsonAdapter} annotation on fields where the value is of
|
||||||
|
* type {@link JsonSerializer} or {@link JsonDeserializer}.
|
||||||
|
*/
|
||||||
|
public final class JsonAdapterSerializerDeserializerTest extends TestCase {
|
||||||
|
|
||||||
|
public void testJsonSerializerDeserializerBasedJsonAdapterOnFields() {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
String json = gson.toJson(new Computer(new User("Inderjeet Singh"), null, new User("Jesse Wilson")));
|
||||||
|
assertEquals("{\"user1\":\"UserSerializer\",\"user3\":\"UserSerializerDeserializer\"}", json);
|
||||||
|
Computer computer = gson.fromJson("{'user2':'Jesse Wilson','user3':'Jake Wharton'}", Computer.class);
|
||||||
|
assertEquals("UserSerializer", computer.user2.name);
|
||||||
|
assertEquals("UserSerializerDeserializer", computer.user3.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Computer {
|
||||||
|
@JsonAdapter(UserSerializer.class) final User user1;
|
||||||
|
@JsonAdapter(UserDeserializer.class) final User user2;
|
||||||
|
@JsonAdapter(UserSerializerDeserializer.class) final User user3;
|
||||||
|
Computer(User user1, User user2, User user3) {
|
||||||
|
this.user1 = user1;
|
||||||
|
this.user2 = user2;
|
||||||
|
this.user3 = user3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class User {
|
||||||
|
public final String name;
|
||||||
|
private User(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class UserSerializer implements JsonSerializer<User> {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("UserSerializer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class UserDeserializer implements JsonDeserializer<User> {
|
||||||
|
@Override
|
||||||
|
public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return new User("UserSerializer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class UserSerializerDeserializer implements JsonSerializer<User>, JsonDeserializer<User> {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(User src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("UserSerializerDeserializer");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public User deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return new User("UserSerializerDeserializer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testJsonSerializerDeserializerBasedJsonAdapterOnClass() {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
String json = gson.toJson(new Computer2(new User2("Inderjeet Singh")));
|
||||||
|
assertEquals("{\"user\":\"UserSerializerDeserializer2\"}", json);
|
||||||
|
Computer2 computer = gson.fromJson("{'user':'Inderjeet Singh'}", Computer2.class);
|
||||||
|
assertEquals("UserSerializerDeserializer2", computer.user.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Computer2 {
|
||||||
|
final User2 user;
|
||||||
|
Computer2(User2 user) {
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonAdapter(UserSerializerDeserializer2.class)
|
||||||
|
private static final class User2 {
|
||||||
|
public final String name;
|
||||||
|
private User2(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class UserSerializerDeserializer2 implements JsonSerializer<User2>, JsonDeserializer<User2> {
|
||||||
|
@Override
|
||||||
|
public JsonElement serialize(User2 src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("UserSerializerDeserializer2");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public User2 deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||||
|
throws JsonParseException {
|
||||||
|
return new User2("UserSerializerDeserializer2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDifferentJsonAdaptersForGenericFieldsOfSameRawType() {
|
||||||
|
Container c = new Container("Foo", 10);
|
||||||
|
Gson gson = new Gson();
|
||||||
|
String json = gson.toJson(c);
|
||||||
|
assertTrue(json.contains("\"a\":\"BaseStringAdapter\""));
|
||||||
|
assertTrue(json.contains("\"b\":\"BaseIntegerAdapter\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Container {
|
||||||
|
@JsonAdapter(BaseStringAdapter.class) Base<String> a;
|
||||||
|
@JsonAdapter(BaseIntegerAdapter.class) Base<Integer> b;
|
||||||
|
Container(String a, int b) {
|
||||||
|
this.a = new Base<String>(a);
|
||||||
|
this.b = new Base<Integer>(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Base<T> {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
T value;
|
||||||
|
Base(T value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class BaseStringAdapter implements JsonSerializer<Base<String>> {
|
||||||
|
@Override public JsonElement serialize(Base<String> src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("BaseStringAdapter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class BaseIntegerAdapter implements JsonSerializer<Base<Integer>> {
|
||||||
|
@Override public JsonElement serialize(Base<Integer> src, Type typeOfSrc, JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("BaseIntegerAdapter");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,7 @@ public final class RuntimeTypeAdapterFactoryFunctionalTest extends TestCase {
|
|||||||
* This test also ensures that {@link TypeAdapterFactory} registered through {@link JsonAdapter}
|
* This test also ensures that {@link TypeAdapterFactory} registered through {@link JsonAdapter}
|
||||||
* work correctly for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)}.
|
* work correctly for {@link Gson#getDelegateAdapter(TypeAdapterFactory, TypeToken)}.
|
||||||
*/
|
*/
|
||||||
public void testSubclassesAutomaticallySerialzed() throws Exception {
|
public void testSubclassesAutomaticallySerialized() throws Exception {
|
||||||
Shape shape = new Circle(25);
|
Shape shape = new Circle(25);
|
||||||
String json = gson.toJson(shape);
|
String json = gson.toJson(shape);
|
||||||
shape = gson.fromJson(json, Shape.class);
|
shape = gson.fromJson(json, Shape.class);
|
||||||
|
Loading…
Reference in New Issue
Block a user