mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 07:59:21 +08:00
Merge pull request #48 from square/jwilson_0601_nullable
Support @Nullable in Adapter Methods.
This commit is contained in:
@@ -50,6 +50,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
|||||||
@Override public void toJson(JsonWriter writer, Object value) throws IOException {
|
@Override public void toJson(JsonWriter writer, Object value) throws IOException {
|
||||||
if (toAdapter == null) {
|
if (toAdapter == null) {
|
||||||
delegate.toJson(writer, value);
|
delegate.toJson(writer, value);
|
||||||
|
} else if (!toAdapter.nullable && value == null) {
|
||||||
|
writer.nullValue();
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
toAdapter.toJson(moshi, writer, value);
|
toAdapter.toJson(moshi, writer, value);
|
||||||
@@ -65,6 +67,9 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
|||||||
@Override public Object fromJson(JsonReader reader) throws IOException {
|
@Override public Object fromJson(JsonReader reader) throws IOException {
|
||||||
if (fromAdapter == null) {
|
if (fromAdapter == null) {
|
||||||
return delegate.fromJson(reader);
|
return delegate.fromJson(reader);
|
||||||
|
} else if (!fromAdapter.nullable && reader.peek() == JsonReader.Token.NULL) {
|
||||||
|
reader.nextNull();
|
||||||
|
return null;
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
return fromAdapter.fromJson(moshi, reader);
|
return fromAdapter.fromJson(moshi, reader);
|
||||||
@@ -132,7 +137,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
|||||||
// public void pointToJson(JsonWriter jsonWriter, Point point) throws Exception {
|
// public void pointToJson(JsonWriter jsonWriter, Point point) throws Exception {
|
||||||
Set<? extends Annotation> parameterAnnotations
|
Set<? extends Annotation> parameterAnnotations
|
||||||
= Util.jsonAnnotations(method.getParameterAnnotations()[1]);
|
= Util.jsonAnnotations(method.getParameterAnnotations()[1]);
|
||||||
return new AdapterMethod(parameterTypes[1], parameterAnnotations, adapter, method) {
|
return new AdapterMethod(parameterTypes[1], parameterAnnotations, adapter, method, false) {
|
||||||
@Override public void toJson(Moshi moshi, JsonWriter writer, Object value)
|
@Override public void toJson(Moshi moshi, JsonWriter writer, Object value)
|
||||||
throws IOException, InvocationTargetException, IllegalAccessException {
|
throws IOException, InvocationTargetException, IllegalAccessException {
|
||||||
method.invoke(adapter, writer, value);
|
method.invoke(adapter, writer, value);
|
||||||
@@ -142,9 +147,11 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
|||||||
} else if (parameterTypes.length == 1 && returnType != void.class) {
|
} else if (parameterTypes.length == 1 && returnType != void.class) {
|
||||||
// public List<Integer> pointToJson(Point point) throws Exception {
|
// public List<Integer> pointToJson(Point point) throws Exception {
|
||||||
final Set<? extends Annotation> returnTypeAnnotations = Util.jsonAnnotations(method);
|
final Set<? extends Annotation> returnTypeAnnotations = Util.jsonAnnotations(method);
|
||||||
Set<? extends Annotation> parameterAnnotations =
|
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||||
Util.jsonAnnotations(method.getParameterAnnotations()[0]);
|
Set<? extends Annotation> qualifierAnnotations =
|
||||||
return new AdapterMethod(parameterTypes[0], parameterAnnotations, adapter, method) {
|
Util.jsonAnnotations(parameterAnnotations[0]);
|
||||||
|
boolean nullable = Util.hasNullable(parameterAnnotations[0]);
|
||||||
|
return new AdapterMethod(parameterTypes[0], qualifierAnnotations, adapter, method, nullable) {
|
||||||
@Override public void toJson(Moshi moshi, JsonWriter writer, Object value)
|
@Override public void toJson(Moshi moshi, JsonWriter writer, Object value)
|
||||||
throws IOException, InvocationTargetException, IllegalAccessException {
|
throws IOException, InvocationTargetException, IllegalAccessException {
|
||||||
JsonAdapter<Object> delegate = moshi.adapter(returnType, returnTypeAnnotations);
|
JsonAdapter<Object> delegate = moshi.adapter(returnType, returnTypeAnnotations);
|
||||||
@@ -175,7 +182,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
|||||||
&& returnType != void.class) {
|
&& returnType != void.class) {
|
||||||
// public Point pointFromJson(JsonReader jsonReader) throws Exception {
|
// public Point pointFromJson(JsonReader jsonReader) throws Exception {
|
||||||
Set<? extends Annotation> returnTypeAnnotations = Util.jsonAnnotations(method);
|
Set<? extends Annotation> returnTypeAnnotations = Util.jsonAnnotations(method);
|
||||||
return new AdapterMethod(returnType, returnTypeAnnotations, adapter, method) {
|
return new AdapterMethod(returnType, returnTypeAnnotations, adapter, method, false) {
|
||||||
@Override public Object fromJson(Moshi moshi, JsonReader reader)
|
@Override public Object fromJson(Moshi moshi, JsonReader reader)
|
||||||
throws IOException, IllegalAccessException, InvocationTargetException {
|
throws IOException, IllegalAccessException, InvocationTargetException {
|
||||||
return method.invoke(adapter, reader);
|
return method.invoke(adapter, reader);
|
||||||
@@ -185,12 +192,14 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
|||||||
} else if (parameterTypes.length == 1 && returnType != void.class) {
|
} else if (parameterTypes.length == 1 && returnType != void.class) {
|
||||||
// public Point pointFromJson(List<Integer> o) throws Exception {
|
// public Point pointFromJson(List<Integer> o) throws Exception {
|
||||||
Set<? extends Annotation> returnTypeAnnotations = Util.jsonAnnotations(method);
|
Set<? extends Annotation> returnTypeAnnotations = Util.jsonAnnotations(method);
|
||||||
final Set<? extends Annotation> parameterAnnotations
|
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||||
= Util.jsonAnnotations(method.getParameterAnnotations()[0]);
|
final Set<? extends Annotation> qualifierAnnotations
|
||||||
return new AdapterMethod(returnType, returnTypeAnnotations, adapter, method) {
|
= Util.jsonAnnotations(parameterAnnotations[0]);
|
||||||
|
boolean nullable = Util.hasNullable(parameterAnnotations[0]);
|
||||||
|
return new AdapterMethod(returnType, returnTypeAnnotations, adapter, method, nullable) {
|
||||||
@Override public Object fromJson(Moshi moshi, JsonReader reader)
|
@Override public Object fromJson(Moshi moshi, JsonReader reader)
|
||||||
throws IOException, IllegalAccessException, InvocationTargetException {
|
throws IOException, IllegalAccessException, InvocationTargetException {
|
||||||
JsonAdapter<Object> delegate = moshi.adapter(parameterTypes[0], parameterAnnotations);
|
JsonAdapter<Object> delegate = moshi.adapter(parameterTypes[0], qualifierAnnotations);
|
||||||
Object intermediate = delegate.fromJson(reader);
|
Object intermediate = delegate.fromJson(reader);
|
||||||
return method.invoke(adapter, intermediate);
|
return method.invoke(adapter, intermediate);
|
||||||
}
|
}
|
||||||
@@ -221,13 +230,15 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
|
|||||||
final Set<? extends Annotation> annotations;
|
final Set<? extends Annotation> annotations;
|
||||||
final Object adapter;
|
final Object adapter;
|
||||||
final Method method;
|
final Method method;
|
||||||
|
final boolean nullable;
|
||||||
|
|
||||||
public AdapterMethod(Type type,
|
public AdapterMethod(Type type,
|
||||||
Set<? extends Annotation> annotations, Object adapter, Method method) {
|
Set<? extends Annotation> annotations, Object adapter, Method method, boolean nullable) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.annotations = annotations;
|
this.annotations = annotations;
|
||||||
this.adapter = adapter;
|
this.adapter = adapter;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
|
this.nullable = nullable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toJson(Moshi moshi, JsonWriter writer, Object value)
|
public void toJson(Moshi moshi, JsonWriter writer, Object value)
|
||||||
|
@@ -53,4 +53,14 @@ final class Util {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if {@code annotations} has any annotation whose simple name is Nullable. */
|
||||||
|
public static boolean hasNullable(Annotation[] annotations) {
|
||||||
|
for (Annotation annotation : annotations) {
|
||||||
|
if (annotation.annotationType().getSimpleName().equals("Nullable")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,8 @@
|
|||||||
package com.squareup.moshi;
|
package com.squareup.moshi;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -181,6 +183,56 @@ public final class AdapterMethodsTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple adapter methods are not invoked for null values unless they're annotated {@code
|
||||||
|
* @Nullable}. (The specific annotation class doesn't matter; just its simple name.)
|
||||||
|
*/
|
||||||
|
@Test public void toAndFromNullNotNullable() throws Exception {
|
||||||
|
Moshi moshi = new Moshi.Builder()
|
||||||
|
.add(new NotNullablePointAsListOfIntegersJsonAdapter())
|
||||||
|
.build();
|
||||||
|
JsonAdapter<Point> pointAdapter = moshi.adapter(Point.class).lenient();
|
||||||
|
assertThat(pointAdapter.toJson(null)).isEqualTo("null");
|
||||||
|
assertThat(pointAdapter.fromJson("null")).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NotNullablePointAsListOfIntegersJsonAdapter {
|
||||||
|
@ToJson List<Integer> pointToJson(Point point) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FromJson Point pointFromJson(List<Integer> o) throws Exception {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void toAndFromNullNullable() throws Exception {
|
||||||
|
Moshi moshi = new Moshi.Builder()
|
||||||
|
.add(new NullablePointAsListOfIntegersJsonAdapter())
|
||||||
|
.build();
|
||||||
|
JsonAdapter<Point> pointAdapter = moshi.adapter(Point.class).lenient();
|
||||||
|
assertThat(pointAdapter.toJson(null)).isEqualTo("[0,0]");
|
||||||
|
assertThat(pointAdapter.fromJson("null")).isEqualTo(new Point(0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NullablePointAsListOfIntegersJsonAdapter {
|
||||||
|
@ToJson List<Integer> pointToJson(@Nullable Point point) {
|
||||||
|
return point != null
|
||||||
|
? Arrays.asList(point.x, point.y)
|
||||||
|
: Arrays.asList(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FromJson Point pointFromJson(@Nullable List<Integer> o) throws Exception {
|
||||||
|
if (o == null) return new Point(0, 0);
|
||||||
|
if (o.size() == 2) return new Point(o.get(0), o.get(1));
|
||||||
|
throw new Exception("Expected null or 2 elements but was " + o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface Nullable {
|
||||||
|
}
|
||||||
|
|
||||||
static class Point {
|
static class Point {
|
||||||
final int x;
|
final int x;
|
||||||
final int y;
|
final int y;
|
||||||
|
Reference in New Issue
Block a user