Don't use a runtime wrapper if a JsonAdapter annotation is present on a field.
This ensures that JsonAdapter annotation works correctly on a primitive field. This is a potentially backward incompatible change.
This commit is contained in:
parent
0f80936ecd
commit
3ff16c30db
@ -24,7 +24,6 @@ import java.text.ParsePosition;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import com.google.gson.internal.bind.util.ISO8601Utils;
|
||||
|
||||
|
@ -72,7 +72,6 @@ public final class Streams {
|
||||
TypeAdapters.JSON_ELEMENT.write(writer, element);
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
public static Writer writerForAppendable(Appendable appendable) {
|
||||
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Type;
|
||||
@ -104,14 +105,22 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
||||
final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
|
||||
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
|
||||
// special casing primitives here saves ~5% on Android...
|
||||
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
||||
TypeAdapter<?> mapped = null;
|
||||
if (annotation != null) {
|
||||
mapped = getTypeAdapter(constructorConstructor, context, fieldType, annotation);
|
||||
}
|
||||
final boolean jsonAdapterPresent = mapped != null;
|
||||
if (mapped == null) mapped = context.getAdapter(fieldType);
|
||||
|
||||
final TypeAdapter<?> typeAdapter = mapped;
|
||||
return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
|
||||
final TypeAdapter<?> typeAdapter = getFieldAdapter(context, field, fieldType);
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
|
||||
@Override void write(JsonWriter writer, Object value)
|
||||
throws IOException, IllegalAccessException {
|
||||
Object fieldValue = field.get(value);
|
||||
TypeAdapter t =
|
||||
new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
|
||||
TypeAdapter t = jsonAdapterPresent ? typeAdapter
|
||||
: new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType());
|
||||
t.write(writer, fieldValue);
|
||||
}
|
||||
@Override void read(JsonReader reader, Object value)
|
||||
@ -129,15 +138,6 @@ public final class ReflectiveTypeAdapterFactory implements TypeAdapterFactory {
|
||||
};
|
||||
}
|
||||
|
||||
TypeAdapter<?> getFieldAdapter(Gson gson, Field field, TypeToken<?> fieldType) {
|
||||
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
|
||||
if (annotation != null) {
|
||||
TypeAdapter<?> adapter = getTypeAdapter(constructorConstructor, gson, fieldType, annotation);
|
||||
if (adapter != null) return adapter;
|
||||
}
|
||||
return gson.getAdapter(fieldType);
|
||||
}
|
||||
|
||||
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
|
||||
Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
|
||||
if (raw.isInterface()) {
|
||||
|
@ -15,14 +15,15 @@
|
||||
*/
|
||||
package com.google.gson.internal.bind;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
|
||||
final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
|
||||
private final Gson context;
|
||||
|
@ -63,7 +63,6 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase {
|
||||
@Override public void write(JsonWriter out, Part part) throws IOException {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override public Part read(JsonReader in) throws IOException {
|
||||
throw new AssertionError();
|
||||
}
|
||||
@ -220,4 +219,53 @@ public final class JsonAdapterAnnotationOnFieldsTest extends TestCase {
|
||||
this.part = part;
|
||||
}
|
||||
}
|
||||
|
||||
/** Regression test contributed through https://github.com/google/gson/issues/831 */
|
||||
public void testNonPrimitiveFieldAnnotationTakesPrecedenceOverDefault() {
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(new GadgetWithOptionalPart(new Part("foo")));
|
||||
assertEquals("{\"part\":\"PartJsonFieldAnnotationAdapter\"}", json);
|
||||
GadgetWithOptionalPart gadget = gson.fromJson("{'part':'foo'}", GadgetWithOptionalPart.class);
|
||||
assertEquals("PartJsonFieldAnnotationAdapter", gadget.part.name);
|
||||
}
|
||||
|
||||
/** Regression test contributed through https://github.com/google/gson/issues/831 */
|
||||
public void testPrimitiveFieldAnnotationTakesPrecedenceOverDefault() {
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(new GadgetWithPrimitivePart(42));
|
||||
assertEquals("{\"part\":\"42\"}", json);
|
||||
GadgetWithPrimitivePart gadget = gson.fromJson(json, GadgetWithPrimitivePart.class);
|
||||
assertEquals(42, gadget.part);
|
||||
}
|
||||
|
||||
private static final class GadgetWithPrimitivePart {
|
||||
@JsonAdapter(LongToStringTypeAdapterFactory.class)
|
||||
final long part;
|
||||
|
||||
private GadgetWithPrimitivePart(long part) {
|
||||
this.part = part;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class LongToStringTypeAdapterFactory implements TypeAdapterFactory {
|
||||
static final TypeAdapter<Long> ADAPTER = new TypeAdapter<Long>() {
|
||||
@Override public void write(JsonWriter out, Long value) throws IOException {
|
||||
out.value(value.toString());
|
||||
}
|
||||
@Override public Long read(JsonReader in) throws IOException {
|
||||
return in.nextLong();
|
||||
}
|
||||
};
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
|
||||
Class<?> cls = type.getRawType();
|
||||
if (Long.class.isAssignableFrom(cls)) {
|
||||
return (TypeAdapter<T>) ADAPTER;
|
||||
} else if (long.class.isAssignableFrom(cls)) {
|
||||
return (TypeAdapter<T>) ADAPTER;
|
||||
}
|
||||
throw new IllegalStateException("Non-long field of type " + type
|
||||
+ " annotated with @JsonAdapter(LongToStringTypeAdapterFactory.class)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user