Updated Gson version to 2.3-SNAPSHOT since this is a new feature.
Added support for a magic field GSON_TYPE_ADAPTER in a class. This adapter is automatically invoked if present. The field must be present in the class (not in any super-type), and must be strongly typed as TypeAdapter<T>.
This commit is contained in:
parent
b2a9d872db
commit
7c97ac2944
@ -3,7 +3,7 @@
|
|||||||
<groupId>com.google.code.gson</groupId>
|
<groupId>com.google.code.gson</groupId>
|
||||||
<artifactId>gson</artifactId>
|
<artifactId>gson</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>2.2.5-SNAPSHOT</version>
|
<version>2.3-SNAPSHOT</version>
|
||||||
<inceptionYear>2008</inceptionYear>
|
<inceptionYear>2008</inceptionYear>
|
||||||
<name>Gson</name>
|
<name>Gson</name>
|
||||||
<parent>
|
<parent>
|
||||||
|
@ -23,6 +23,7 @@ import com.google.gson.internal.Streams;
|
|||||||
import com.google.gson.internal.bind.ArrayTypeAdapter;
|
import com.google.gson.internal.bind.ArrayTypeAdapter;
|
||||||
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
|
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
|
||||||
import com.google.gson.internal.bind.DateTypeAdapter;
|
import com.google.gson.internal.bind.DateTypeAdapter;
|
||||||
|
import com.google.gson.internal.bind.FieldTypeAdapterFactory;
|
||||||
import com.google.gson.internal.bind.JsonTreeReader;
|
import com.google.gson.internal.bind.JsonTreeReader;
|
||||||
import com.google.gson.internal.bind.JsonTreeWriter;
|
import com.google.gson.internal.bind.JsonTreeWriter;
|
||||||
import com.google.gson.internal.bind.MapTypeAdapterFactory;
|
import com.google.gson.internal.bind.MapTypeAdapterFactory;
|
||||||
@ -237,6 +238,7 @@ 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 FieldTypeAdapterFactory());
|
||||||
factories.add(new ReflectiveTypeAdapterFactory(
|
factories.add(new ReflectiveTypeAdapterFactory(
|
||||||
constructorConstructor, fieldNamingPolicy, excluder));
|
constructorConstructor, fieldNamingPolicy, excluder));
|
||||||
|
|
||||||
|
@ -391,6 +391,22 @@ public final class $Gson$Types {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a parameterized type A<B,C>, returns B. If the specified type is not
|
||||||
|
* a generic type, returns null.
|
||||||
|
*/
|
||||||
|
public static Type getFirstTypeArgument(Type type) {
|
||||||
|
try {
|
||||||
|
if (!(type instanceof ParameterizedType)) return null;
|
||||||
|
ParameterizedType ptype = (ParameterizedType) type;
|
||||||
|
Type[] actualTypeArguments = ptype.getActualTypeArguments();
|
||||||
|
if (actualTypeArguments.length == 0) return null;
|
||||||
|
return canonicalize(actualTypeArguments[0]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
|
static Type resolveTypeVariable(Type context, Class<?> contextRawType, TypeVariable<?> unknown) {
|
||||||
Class<?> declaredByRaw = declaringClassOf(unknown);
|
Class<?> declaredByRaw = declaringClassOf(unknown);
|
||||||
|
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.gson.internal.bind;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.TypeAdapterFactory;
|
||||||
|
import com.google.gson.internal.$Gson$Types;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a type T, looks for the magic static field named GSON_TYPE_ADAPTER of type
|
||||||
|
* TypeAdapter<T> and uses it as the default type adapter.
|
||||||
|
*
|
||||||
|
* @since 2.3
|
||||||
|
*/
|
||||||
|
public final class FieldTypeAdapterFactory implements TypeAdapterFactory {
|
||||||
|
private static final String FIELD_ADAPTER_NAME = "GSON_TYPE_ADAPTER";
|
||||||
|
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
|
||||||
|
Class<? super T> clazz = targetType.getRawType();
|
||||||
|
try {
|
||||||
|
Field typeAdapterField = clazz.getDeclaredField(FIELD_ADAPTER_NAME);
|
||||||
|
typeAdapterField.setAccessible(true);
|
||||||
|
if (!Modifier.isStatic(typeAdapterField.getModifiers())) return null;
|
||||||
|
Object fieldAdapterValue = typeAdapterField.get(null);
|
||||||
|
if (fieldAdapterValue != null && fieldAdapterValue instanceof TypeAdapter) {
|
||||||
|
// We know that the GSON_TYPE_ADAPTER field is of type TypeAdapter.
|
||||||
|
// However, we need to assert that its type variable TypeAdapter<TypeVariable> matches
|
||||||
|
// the target type
|
||||||
|
Type fieldTypeVariable = $Gson$Types.getFirstTypeArgument(typeAdapterField.getGenericType());
|
||||||
|
if (targetType.getType().equals(fieldTypeVariable)) {
|
||||||
|
return (TypeAdapter) fieldAdapterValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) { // ignore1
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 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.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.JsonSerializationContext;
|
||||||
|
import com.google.gson.JsonSerializer;
|
||||||
|
import com.google.gson.TypeAdapter;
|
||||||
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functional tests for the magic field GSON_TYPE_ADAPTER present in a class.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public final class GsonFieldTypeAdapterTest extends TestCase {
|
||||||
|
|
||||||
|
public void testFieldAdapterInvoked() {
|
||||||
|
String json = new Gson().toJson(new ClassWithFieldAdapter("bar"));
|
||||||
|
assertEquals("\"fieldAdapter\"", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRegisteredAdapterOverridesFieldAdapter() {
|
||||||
|
TypeAdapter<ClassWithFieldAdapter> typeAdapter = new TypeAdapter<ClassWithFieldAdapter>() {
|
||||||
|
@Override public void write(JsonWriter out, ClassWithFieldAdapter value) throws IOException {
|
||||||
|
out.value("registeredAdapter");
|
||||||
|
}
|
||||||
|
@Override public ClassWithFieldAdapter read(JsonReader in) throws IOException {
|
||||||
|
return new ClassWithFieldAdapter(in.nextString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(ClassWithFieldAdapter.class, typeAdapter)
|
||||||
|
.create();
|
||||||
|
String json = gson.toJson(new ClassWithFieldAdapter("abcd"));
|
||||||
|
assertEquals("\"registeredAdapter\"", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The serializer overrides field adapter, but for deserializer the fieldAdapter is used.
|
||||||
|
*/
|
||||||
|
public void testRegisteredSerializerOverridesFieldAdapter() {
|
||||||
|
JsonSerializer<ClassWithFieldAdapter> serializer = new JsonSerializer<ClassWithFieldAdapter>() {
|
||||||
|
public JsonElement serialize(ClassWithFieldAdapter src, Type typeOfSrc,
|
||||||
|
JsonSerializationContext context) {
|
||||||
|
return new JsonPrimitive("registeredSerializer");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(ClassWithFieldAdapter.class, serializer)
|
||||||
|
.create();
|
||||||
|
String json = gson.toJson(new ClassWithFieldAdapter("abcd"));
|
||||||
|
assertEquals("\"registeredSerializer\"", json);
|
||||||
|
ClassWithFieldAdapter target = gson.fromJson("abcd", ClassWithFieldAdapter.class);
|
||||||
|
assertEquals("fieldAdapter", target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The deserializer overrides field adapter, but for serializer the fieldAdapter is used.
|
||||||
|
*/
|
||||||
|
public void testRegisteredDeserializerOverridesFieldAdapter() {
|
||||||
|
JsonDeserializer<ClassWithFieldAdapter> deserializer = new JsonDeserializer<ClassWithFieldAdapter>() {
|
||||||
|
public ClassWithFieldAdapter deserialize(JsonElement json, Type typeOfT,
|
||||||
|
JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
return new ClassWithFieldAdapter("registeredDeserializer");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Gson gson = new GsonBuilder()
|
||||||
|
.registerTypeAdapter(ClassWithFieldAdapter.class, deserializer)
|
||||||
|
.create();
|
||||||
|
String json = gson.toJson(new ClassWithFieldAdapter("abcd"));
|
||||||
|
assertEquals("\"fieldAdapter\"", json);
|
||||||
|
ClassWithFieldAdapter target = gson.fromJson("abcd", ClassWithFieldAdapter.class);
|
||||||
|
assertEquals("registeredDeserializer", target.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testFieldAdapterNotInvokedIfNull() {
|
||||||
|
String json = new Gson().toJson(new ClassWithNullFieldAdapter("bar"));
|
||||||
|
assertEquals("{\"value\":\"bar\"}", json);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNonStaticFieldAdapterNotInvoked() {
|
||||||
|
String json = new Gson().toJson(new ClassWithNonStaticFieldAdapter("bar"));
|
||||||
|
assertFalse(json.contains("fieldAdapter"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIncorrectTypeAdapterNotInvoked() {
|
||||||
|
String json = new Gson().toJson(new ClassWithIncorrectFieldAdapter("bar"));
|
||||||
|
assertFalse(json.contains("fieldAdapter"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSuperclassTypeAdapterNotInvoked() {
|
||||||
|
String json = new Gson().toJson(new ClassWithSuperClassFieldAdapter("bar"));
|
||||||
|
assertFalse(json.contains("fieldAdapter"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ClassWithFieldAdapter {
|
||||||
|
final String value;
|
||||||
|
ClassWithFieldAdapter(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
private static final TypeAdapter<ClassWithFieldAdapter> GSON_TYPE_ADAPTER =
|
||||||
|
new TypeAdapter<GsonFieldTypeAdapterTest.ClassWithFieldAdapter>() {
|
||||||
|
@Override public void write(JsonWriter out, ClassWithFieldAdapter value) throws IOException {
|
||||||
|
out.value("fieldAdapter");
|
||||||
|
}
|
||||||
|
@Override public ClassWithFieldAdapter read(JsonReader in) throws IOException {
|
||||||
|
in.nextString();
|
||||||
|
return new ClassWithFieldAdapter("fieldAdapter");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ClassWithSuperClassFieldAdapter extends ClassWithFieldAdapter {
|
||||||
|
ClassWithSuperClassFieldAdapter(String value) {
|
||||||
|
super(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ClassWithNullFieldAdapter {
|
||||||
|
final String value;
|
||||||
|
ClassWithNullFieldAdapter(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
private static final TypeAdapter<ClassWithFieldAdapter> GSON_TYPE_ADAPTER = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ClassWithNonStaticFieldAdapter {
|
||||||
|
final String value;
|
||||||
|
ClassWithNonStaticFieldAdapter(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
private final TypeAdapter<ClassWithNonStaticFieldAdapter> GSON_TYPE_ADAPTER =
|
||||||
|
new TypeAdapter<ClassWithNonStaticFieldAdapter>() {
|
||||||
|
@Override public void write(JsonWriter out, ClassWithNonStaticFieldAdapter value) throws IOException {
|
||||||
|
out.value("fieldAdapter");
|
||||||
|
}
|
||||||
|
@Override public ClassWithNonStaticFieldAdapter read(JsonReader in) throws IOException {
|
||||||
|
in.nextString();
|
||||||
|
return new ClassWithNonStaticFieldAdapter("fieldAdapter");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ClassWithIncorrectFieldAdapter {
|
||||||
|
final String value;
|
||||||
|
ClassWithIncorrectFieldAdapter(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
// Note that the type is NOT TypeAdapter<ClassWithIncorrectFieldAdapter> so this
|
||||||
|
// field should be ignored.
|
||||||
|
private static final TypeAdapter<ClassWithFieldAdapter> GSON_TYPE_ADAPTER =
|
||||||
|
new TypeAdapter<GsonFieldTypeAdapterTest.ClassWithFieldAdapter>() {
|
||||||
|
@Override public void write(JsonWriter out, ClassWithFieldAdapter value) throws IOException {
|
||||||
|
out.value("fieldAdapter");
|
||||||
|
}
|
||||||
|
@Override public ClassWithFieldAdapter read(JsonReader in) throws IOException {
|
||||||
|
in.nextString();
|
||||||
|
return new ClassWithFieldAdapter("fieldAdapter");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Google Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.google.gson.internal;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public final class GsonTypesTest extends TestCase {
|
||||||
|
|
||||||
|
public void testNewParameterizedTypeWithoutOwner() {
|
||||||
|
// List<A>. List is a top-level class
|
||||||
|
Type type = $Gson$Types.newParameterizedTypeWithOwner(null, List.class, A.class);
|
||||||
|
assertEquals(A.class, $Gson$Types.getFirstTypeArgument(type));
|
||||||
|
|
||||||
|
// A<B>. A is a static inner class.
|
||||||
|
type = $Gson$Types.newParameterizedTypeWithOwner(null, A.class, B.class);
|
||||||
|
assertEquals(B.class, $Gson$Types.getFirstTypeArgument(type));
|
||||||
|
|
||||||
|
final class D {
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// D<A> is not allowed since D is not a static inner class
|
||||||
|
$Gson$Types.newParameterizedTypeWithOwner(null, D.class, A.class);
|
||||||
|
} catch (IllegalArgumentException expected) {}
|
||||||
|
|
||||||
|
// A<D> is allowed.
|
||||||
|
type = $Gson$Types.newParameterizedTypeWithOwner(null, A.class, D.class);
|
||||||
|
assertEquals(D.class, $Gson$Types.getFirstTypeArgument(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testGetFirstTypeArgument() {
|
||||||
|
assertNull($Gson$Types.getFirstTypeArgument(A.class));
|
||||||
|
|
||||||
|
Type type = $Gson$Types.newParameterizedTypeWithOwner(null, A.class, B.class, C.class);
|
||||||
|
assertEquals(B.class, $Gson$Types.getFirstTypeArgument(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class A {
|
||||||
|
}
|
||||||
|
private static final class B {
|
||||||
|
}
|
||||||
|
private static final class C {
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user