Skip to content

Commit bb451ea

Browse files
committed
Merge pull request google#832 from google/831
Ensuring that JsonAdapter annotation works correctly for primitive fi…
2 parents ecda358 + 3ff16c3 commit bb451ea

File tree

5 files changed

+65
-18
lines changed

5 files changed

+65
-18
lines changed

gson/src/main/java/com/google/gson/DefaultDateTypeAdapter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import java.text.SimpleDateFormat;
2525
import java.util.Date;
2626
import java.util.Locale;
27-
import java.util.TimeZone;
2827

2928
import com.google.gson.internal.bind.util.ISO8601Utils;
3029

gson/src/main/java/com/google/gson/internal/Streams.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ public static void write(JsonElement element, JsonWriter writer) throws IOExcept
7272
TypeAdapters.JSON_ELEMENT.write(writer, element);
7373
}
7474

75-
@SuppressWarnings("resource")
7675
public static Writer writerForAppendable(Appendable appendable) {
7776
return appendable instanceof Writer ? (Writer) appendable : new AppendableWriter(appendable);
7877
}

gson/src/main/java/com/google/gson/internal/bind/ReflectiveTypeAdapterFactory.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import com.google.gson.stream.JsonReader;
3333
import com.google.gson.stream.JsonToken;
3434
import com.google.gson.stream.JsonWriter;
35+
3536
import java.io.IOException;
3637
import java.lang.reflect.Field;
3738
import java.lang.reflect.Type;
@@ -104,14 +105,22 @@ private ReflectiveTypeAdapterFactory.BoundField createBoundField(
104105
final TypeToken<?> fieldType, boolean serialize, boolean deserialize) {
105106
final boolean isPrimitive = Primitives.isPrimitive(fieldType.getRawType());
106107
// special casing primitives here saves ~5% on Android...
108+
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
109+
TypeAdapter<?> mapped = null;
110+
if (annotation != null) {
111+
mapped = getTypeAdapter(constructorConstructor, context, fieldType, annotation);
112+
}
113+
final boolean jsonAdapterPresent = mapped != null;
114+
if (mapped == null) mapped = context.getAdapter(fieldType);
115+
116+
final TypeAdapter<?> typeAdapter = mapped;
107117
return new ReflectiveTypeAdapterFactory.BoundField(name, serialize, deserialize) {
108-
final TypeAdapter<?> typeAdapter = getFieldAdapter(context, field, fieldType);
109118
@SuppressWarnings({"unchecked", "rawtypes"}) // the type adapter and field type always agree
110119
@Override void write(JsonWriter writer, Object value)
111120
throws IOException, IllegalAccessException {
112121
Object fieldValue = field.get(value);
113-
TypeAdapter t =
114-
new TypeAdapterRuntimeTypeWrapper(context, this.typeAdapter, fieldType.getType());
122+
TypeAdapter t = jsonAdapterPresent ? typeAdapter
123+
: new TypeAdapterRuntimeTypeWrapper(context, typeAdapter, fieldType.getType());
115124
t.write(writer, fieldValue);
116125
}
117126
@Override void read(JsonReader reader, Object value)
@@ -129,15 +138,6 @@ private ReflectiveTypeAdapterFactory.BoundField createBoundField(
129138
};
130139
}
131140

132-
TypeAdapter<?> getFieldAdapter(Gson gson, Field field, TypeToken<?> fieldType) {
133-
JsonAdapter annotation = field.getAnnotation(JsonAdapter.class);
134-
if (annotation != null) {
135-
TypeAdapter<?> adapter = getTypeAdapter(constructorConstructor, gson, fieldType, annotation);
136-
if (adapter != null) return adapter;
137-
}
138-
return gson.getAdapter(fieldType);
139-
}
140-
141141
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
142142
Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
143143
if (raw.isInterface()) {

gson/src/main/java/com/google/gson/internal/bind/TypeAdapterRuntimeTypeWrapper.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
*/
1616
package com.google.gson.internal.bind;
1717

18+
import java.io.IOException;
19+
import java.lang.reflect.Type;
20+
import java.lang.reflect.TypeVariable;
21+
1822
import com.google.gson.Gson;
1923
import com.google.gson.TypeAdapter;
2024
import com.google.gson.reflect.TypeToken;
2125
import com.google.gson.stream.JsonReader;
2226
import com.google.gson.stream.JsonWriter;
23-
import java.io.IOException;
24-
import java.lang.reflect.Type;
25-
import java.lang.reflect.TypeVariable;
2627

2728
final class TypeAdapterRuntimeTypeWrapper<T> extends TypeAdapter<T> {
2829
private final Gson context;

gson/src/test/java/com/google/gson/functional/JsonAdapterAnnotationOnFieldsTest.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ public void testFieldAnnotationTakesPrecedenceOverRegisteredTypeAdapter() {
6363
@Override public void write(JsonWriter out, Part part) throws IOException {
6464
throw new AssertionError();
6565
}
66-
6766
@Override public Part read(JsonReader in) throws IOException {
6867
throw new AssertionError();
6968
}
@@ -220,4 +219,53 @@ private GadgetWithOptionalPart(Part part) {
220219
this.part = part;
221220
}
222221
}
222+
223+
/** Regression test contributed through https://github.com/google/gson/issues/831 */
224+
public void testNonPrimitiveFieldAnnotationTakesPrecedenceOverDefault() {
225+
Gson gson = new Gson();
226+
String json = gson.toJson(new GadgetWithOptionalPart(new Part("foo")));
227+
assertEquals("{\"part\":\"PartJsonFieldAnnotationAdapter\"}", json);
228+
GadgetWithOptionalPart gadget = gson.fromJson("{'part':'foo'}", GadgetWithOptionalPart.class);
229+
assertEquals("PartJsonFieldAnnotationAdapter", gadget.part.name);
230+
}
231+
232+
/** Regression test contributed through https://github.com/google/gson/issues/831 */
233+
public void testPrimitiveFieldAnnotationTakesPrecedenceOverDefault() {
234+
Gson gson = new Gson();
235+
String json = gson.toJson(new GadgetWithPrimitivePart(42));
236+
assertEquals("{\"part\":\"42\"}", json);
237+
GadgetWithPrimitivePart gadget = gson.fromJson(json, GadgetWithPrimitivePart.class);
238+
assertEquals(42, gadget.part);
239+
}
240+
241+
private static final class GadgetWithPrimitivePart {
242+
@JsonAdapter(LongToStringTypeAdapterFactory.class)
243+
final long part;
244+
245+
private GadgetWithPrimitivePart(long part) {
246+
this.part = part;
247+
}
248+
}
249+
250+
private static final class LongToStringTypeAdapterFactory implements TypeAdapterFactory {
251+
static final TypeAdapter<Long> ADAPTER = new TypeAdapter<Long>() {
252+
@Override public void write(JsonWriter out, Long value) throws IOException {
253+
out.value(value.toString());
254+
}
255+
@Override public Long read(JsonReader in) throws IOException {
256+
return in.nextLong();
257+
}
258+
};
259+
@SuppressWarnings("unchecked")
260+
@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
261+
Class<?> cls = type.getRawType();
262+
if (Long.class.isAssignableFrom(cls)) {
263+
return (TypeAdapter<T>) ADAPTER;
264+
} else if (long.class.isAssignableFrom(cls)) {
265+
return (TypeAdapter<T>) ADAPTER;
266+
}
267+
throw new IllegalStateException("Non-long field of type " + type
268+
+ " annotated with @JsonAdapter(LongToStringTypeAdapterFactory.class)");
269+
}
270+
}
223271
}

0 commit comments

Comments
 (0)