Created a JsonAdapter annotation that can be applied to classes to indicate their TypeAdapter.
This commit is contained in:
parent
7c97ac2944
commit
67d512ee7d
|
@ -24,6 +24,7 @@ import com.google.gson.internal.bind.ArrayTypeAdapter;
|
|||
import com.google.gson.internal.bind.CollectionTypeAdapterFactory;
|
||||
import com.google.gson.internal.bind.DateTypeAdapter;
|
||||
import com.google.gson.internal.bind.FieldTypeAdapterFactory;
|
||||
import com.google.gson.internal.bind.JsonAdapterAnnotationTypeAdapterFactory;
|
||||
import com.google.gson.internal.bind.JsonTreeReader;
|
||||
import com.google.gson.internal.bind.JsonTreeWriter;
|
||||
import com.google.gson.internal.bind.MapTypeAdapterFactory;
|
||||
|
@ -238,6 +239,7 @@ public final class Gson {
|
|||
// type adapters for composite and user-defined types
|
||||
factories.add(new CollectionTypeAdapterFactory(constructorConstructor));
|
||||
factories.add(new MapTypeAdapterFactory(constructorConstructor, complexMapKeySerialization));
|
||||
factories.add(new JsonAdapterAnnotationTypeAdapterFactory(constructorConstructor));
|
||||
factories.add(new FieldTypeAdapterFactory());
|
||||
factories.add(new ReflectiveTypeAdapterFactory(
|
||||
constructorConstructor, fieldNamingPolicy, excluder));
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.annotations;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.google.gson.TypeAdapter;
|
||||
|
||||
/**
|
||||
* An annotation that indicates the Gson {@link TypeAdapter} to use with a class or a field.
|
||||
* Any type adapters registered in {@link com.google.gson.GsonBuilder} supersede the adapter
|
||||
* specified in this annotation.
|
||||
*
|
||||
* <p>Here is an example of how this annotation is used:</p>
|
||||
* <pre>
|
||||
* @JsonAdapter(UserJsonAdapter.class)
|
||||
* public class User {
|
||||
* public final String firstName, lastName;
|
||||
* private User(String firstName, String lastName) {
|
||||
* this.firstName = firstName;
|
||||
* this.lastName = lastName;
|
||||
* }
|
||||
* }
|
||||
* public class UserJsonAdapter extends TypeAdapter<User> {
|
||||
* @Override public void write(JsonWriter out, User user) throws IOException {
|
||||
* // implement write: combine firstName and lastName into name
|
||||
* out.beginObject();
|
||||
* out.name("name");
|
||||
* out.value(user.firstName + " " + user.lastName);
|
||||
* out.endObject();
|
||||
* // implement the write method
|
||||
* }
|
||||
* @Override public User read(JsonReader in) throws IOException {
|
||||
* // implement read: split name into firstName and lastName
|
||||
* in.beginObject();
|
||||
* in.nextName();
|
||||
* String[] nameParts = in.nextString().split(" ");
|
||||
* in.endObject();
|
||||
* return new User(nameParts[0], nameParts[1]);
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Since User class specified UserJsonAdapter.class in @JsonAdapter annotation, it
|
||||
* will automatically be invoked to serialize/deserialize User instances.
|
||||
*
|
||||
* If the UserJsonAdapter needs a constructor other than a no-args constructor, you must register
|
||||
* an {@link com.google.gson.InstanceCreator} for it.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @author Inderjeet Singh
|
||||
* @author Joel Leitch
|
||||
* @author Jesse Leitch
|
||||
*/
|
||||
// Note that the above example is taken from JsonAdapterANnotationTest.
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
public @interface JsonAdapter {
|
||||
|
||||
Class<? extends TypeAdapter<?>> value();
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.annotations.JsonAdapter;
|
||||
import com.google.gson.internal.ConstructorConstructor;
|
||||
import com.google.gson.internal.ObjectConstructor;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
/**
|
||||
* Given a type T, looks for the annotation {@link JsonAdapter} and uses an instance of the
|
||||
* specified class as the default type adapter.
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public final class JsonAdapterAnnotationTypeAdapterFactory implements TypeAdapterFactory {
|
||||
|
||||
private final ConstructorConstructor constructorConstructor;
|
||||
|
||||
public JsonAdapterAnnotationTypeAdapterFactory(ConstructorConstructor constructorConstructor) {
|
||||
this.constructorConstructor = constructorConstructor;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> targetType) {
|
||||
Class<? super T> clazz = targetType.getRawType();
|
||||
JsonAdapter annotation = clazz.getAnnotation(JsonAdapter.class);
|
||||
if (annotation == null) return null;
|
||||
Class<? extends TypeAdapter<?>> adapterClass = annotation.value();
|
||||
ObjectConstructor<? extends TypeAdapter<?>> constructor = constructorConstructor.get(TypeToken.get(adapterClass));
|
||||
TypeAdapter adapter = constructor.construct();
|
||||
return adapter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* 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.annotations.JsonAdapter;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Functional tests for the {@link com.google.gson.annotations.JsonAdapter} annotation.
|
||||
*/
|
||||
public final class JsonAdapterAnnotationTest extends TestCase {
|
||||
|
||||
public void testJsonAdapterInvoked() {
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(new A("bar"));
|
||||
assertEquals("\"jsonAdapter\"", json);
|
||||
|
||||
// Also invoke the JsonAdapter javadoc sample
|
||||
json = gson.toJson(new User("Inderjeet", "Singh"));
|
||||
assertEquals("{\"name\":\"Inderjeet Singh\"}", json);
|
||||
User user = gson.fromJson("{'name':'Joel Leitch'}", User.class);
|
||||
assertEquals("Joel", user.firstName);
|
||||
assertEquals("Leitch", user.lastName);
|
||||
}
|
||||
|
||||
public void testRegisteredAdapterOverridesJsonAdapter() {
|
||||
TypeAdapter<A> typeAdapter = new TypeAdapter<A>() {
|
||||
@Override public void write(JsonWriter out, A value) throws IOException {
|
||||
out.value("registeredAdapter");
|
||||
}
|
||||
@Override public A read(JsonReader in) throws IOException {
|
||||
return new A(in.nextString());
|
||||
}
|
||||
};
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(A.class, typeAdapter)
|
||||
.create();
|
||||
String json = gson.toJson(new A("abcd"));
|
||||
assertEquals("\"registeredAdapter\"", json);
|
||||
}
|
||||
|
||||
/**
|
||||
* The serializer overrides field adapter, but for deserializer the fieldAdapter is used.
|
||||
*/
|
||||
public void testRegisteredSerializerOverridesJsonAdapter() {
|
||||
JsonSerializer<A> serializer = new JsonSerializer<A>() {
|
||||
public JsonElement serialize(A src, Type typeOfSrc,
|
||||
JsonSerializationContext context) {
|
||||
return new JsonPrimitive("registeredSerializer");
|
||||
}
|
||||
};
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(A.class, serializer)
|
||||
.create();
|
||||
String json = gson.toJson(new A("abcd"));
|
||||
assertEquals("\"registeredSerializer\"", json);
|
||||
A target = gson.fromJson("abcd", A.class);
|
||||
assertEquals("jsonAdapter", target.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The deserializer overrides Json adapter, but for serializer the jsonAdapter is used.
|
||||
*/
|
||||
public void testRegisteredDeserializerOverridesJsonAdapter() {
|
||||
JsonDeserializer<A> deserializer = new JsonDeserializer<A>() {
|
||||
public A deserialize(JsonElement json, Type typeOfT,
|
||||
JsonDeserializationContext context) throws JsonParseException {
|
||||
return new A("registeredDeserializer");
|
||||
}
|
||||
};
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(A.class, deserializer)
|
||||
.create();
|
||||
String json = gson.toJson(new A("abcd"));
|
||||
assertEquals("\"jsonAdapter\"", json);
|
||||
A target = gson.fromJson("abcd", A.class);
|
||||
assertEquals("registeredDeserializer", target.value);
|
||||
}
|
||||
|
||||
public void testIncorrectTypeAdapterFails() {
|
||||
try {
|
||||
String json = new Gson().toJson(new ClassWithIncorrectJsonAdapter("bar"));
|
||||
fail(json);
|
||||
} catch (ClassCastException expected) {}
|
||||
}
|
||||
|
||||
public void testSuperclassTypeAdapterNotInvoked() {
|
||||
String json = new Gson().toJson(new B("bar"));
|
||||
assertFalse(json.contains("jsonAdapter"));
|
||||
}
|
||||
|
||||
@JsonAdapter(A.JsonAdapter.class)
|
||||
private static class A {
|
||||
final String value;
|
||||
A(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
private static final class JsonAdapter extends TypeAdapter<A> {
|
||||
@Override public void write(JsonWriter out, A value) throws IOException {
|
||||
out.value("jsonAdapter");
|
||||
}
|
||||
@Override public A read(JsonReader in) throws IOException {
|
||||
in.nextString();
|
||||
return new A("jsonAdapter");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class B extends A {
|
||||
B(String value) {
|
||||
super(value);
|
||||
}
|
||||
}
|
||||
// Note that the type is NOT TypeAdapter<ClassWithIncorrectJsonAdapter> so this
|
||||
// should cause error
|
||||
@JsonAdapter(A.JsonAdapter.class)
|
||||
private static final class ClassWithIncorrectJsonAdapter {
|
||||
@SuppressWarnings("unused") final String value;
|
||||
ClassWithIncorrectJsonAdapter(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
// This class is used in JsonAdapter Javadoc as an example
|
||||
@JsonAdapter(UserJsonAdapter.class)
|
||||
private static class User {
|
||||
public final String firstName, lastName;
|
||||
private User(String firstName, String lastName) {
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
}
|
||||
private static class UserJsonAdapter extends TypeAdapter<User> {
|
||||
@Override public void write(JsonWriter out, User user) throws IOException {
|
||||
// implement write: combine firstName and lastName into name
|
||||
out.beginObject();
|
||||
out.name("name");
|
||||
out.value(user.firstName + " " + user.lastName);
|
||||
out.endObject();
|
||||
// implement the write method
|
||||
}
|
||||
@Override public User read(JsonReader in) throws IOException {
|
||||
// implement read: split name into firstName and lastName
|
||||
in.beginObject();
|
||||
in.nextName();
|
||||
String[] nameParts = in.nextString().split(" ");
|
||||
in.endObject();
|
||||
return new User(nameParts[0], nameParts[1]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user