Import jsr305 and use it to mark @Nullable stuff. (#297)

This commit is contained in:
Jesse Wilson
2017-05-06 20:31:24 -04:00
committed by GitHub
parent dac5f695b3
commit c65b3bf1cb
26 changed files with 149 additions and 92 deletions

View File

@@ -12,6 +12,11 @@
<artifactId>moshi-examples</artifactId> <artifactId>moshi-examples</artifactId>
<dependencies> <dependencies>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>com.squareup.moshi</groupId> <groupId>com.squareup.moshi</groupId>
<artifactId>moshi</artifactId> <artifactId>moshi</artifactId>

View File

@@ -24,6 +24,7 @@ import java.io.IOException;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
public final class DefaultOnDataMismatchAdapter<T> extends JsonAdapter<T> { public final class DefaultOnDataMismatchAdapter<T> extends JsonAdapter<T> {
private final JsonAdapter<T> delegate; private final JsonAdapter<T> delegate;
@@ -54,7 +55,7 @@ public final class DefaultOnDataMismatchAdapter<T> extends JsonAdapter<T> {
public static <T> Factory newFactory(final Class<T> type, final T defaultValue) { public static <T> Factory newFactory(final Class<T> type, final T defaultValue) {
return new Factory() { return new Factory() {
@Override public JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
Type requestedType, Set<? extends Annotation> annotations, Moshi moshi) { Type requestedType, Set<? extends Annotation> annotations, Moshi moshi) {
if (type != requestedType) return null; if (type != requestedType) return null;
JsonAdapter<T> delegate = moshi.nextAdapter(this, type, annotations); JsonAdapter<T> delegate = moshi.nextAdapter(this, type, annotations);

View File

@@ -28,6 +28,7 @@ import java.lang.annotation.Annotation;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -51,8 +52,8 @@ final class Unwrap {
public static final class EnvelopeJsonAdapter extends JsonAdapter<Object> { public static final class EnvelopeJsonAdapter extends JsonAdapter<Object> {
public static final JsonAdapter.Factory FACTORY = new Factory() { public static final JsonAdapter.Factory FACTORY = new Factory() {
@Override @Override public @Nullable JsonAdapter<?> create(
public JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi) { Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Set<? extends Annotation> delegateAnnotations = Set<? extends Annotation> delegateAnnotations =
Types.nextAnnotations(annotations, Enveloped.class); Types.nextAnnotations(annotations, Enveloped.class);
if (delegateAnnotations == null) { if (delegateAnnotations == null) {

View File

@@ -0,0 +1,3 @@
/** Moshi code samples. */
@javax.annotation.ParametersAreNonnullByDefault
package com.squareup.moshi.recipes;

View File

@@ -95,7 +95,9 @@ internal class KotlinJsonAdapter<T>(
return result return result
} }
override fun toJson(writer: JsonWriter, value: T) { override fun toJson(writer: JsonWriter, value: T?) {
if (value == null) throw NullPointerException("value == null")
writer.beginObject() writer.beginObject()
for (binding in bindings) { for (binding in bindings) {
if (binding == null) continue // Skip constructor parameters that aren't properties. if (binding == null) continue // Skip constructor parameters that aren't properties.
@@ -202,6 +204,6 @@ object KotlinJsonAdapterFactory : JsonAdapter.Factory {
bindings += bindingsByName.values bindings += bindingsByName.values
val options = JsonReader.Options.of(*bindings.map { it?.name ?: "\u0000" }.toTypedArray()) val options = JsonReader.Options.of(*bindings.map { it?.name ?: "\u0000" }.toTypedArray())
return KotlinJsonAdapter(constructor, bindings, options) return KotlinJsonAdapter(constructor, bindings, options).nullSafe()
} }
} }

View File

@@ -31,7 +31,7 @@ class KotlinJsonAdapterTest {
val encoded = ConstructorParameters(3, 5) val encoded = ConstructorParameters(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -47,7 +47,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5 encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}") val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}")!!
assertThat(decoded.a).isEqualTo(3) assertThat(decoded.a).isEqualTo(3)
assertThat(decoded.b).isEqualTo(5) assertThat(decoded.b).isEqualTo(5)
} }
@@ -65,7 +65,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5 encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -81,7 +81,7 @@ class KotlinJsonAdapterTest {
val encoded = ImmutableConstructorParameters(3, 5) val encoded = ImmutableConstructorParameters(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -95,7 +95,7 @@ class KotlinJsonAdapterTest {
val encoded = ImmutableProperties(3, 5) val encoded = ImmutableProperties(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}") val decoded = jsonAdapter.fromJson("{\"a\":3,\"b\":5}")!!
assertThat(decoded.a).isEqualTo(3) assertThat(decoded.a).isEqualTo(3)
assertThat(decoded.b).isEqualTo(5) assertThat(decoded.b).isEqualTo(5)
} }
@@ -112,7 +112,7 @@ class KotlinJsonAdapterTest {
val encoded = ConstructorDefaultValues(3, 5) val encoded = ConstructorDefaultValues(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"b\":6}") val decoded = jsonAdapter.fromJson("{\"b\":6}")!!
assertThat(decoded.a).isEqualTo(-1) assertThat(decoded.a).isEqualTo(-1)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -155,7 +155,7 @@ class KotlinJsonAdapterTest {
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
assertThat(jsonAdapter.serializeNulls().toJson(encoded)).isEqualTo("{\"a\":null,\"b\":5}") assertThat(jsonAdapter.serializeNulls().toJson(encoded)).isEqualTo("{\"a\":null,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":null,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":null,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(null) assertThat(decoded.a).isEqualTo(null)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -170,7 +170,7 @@ class KotlinJsonAdapterTest {
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
assertThat(jsonAdapter.serializeNulls().toJson(encoded)).isEqualTo("{\"a\":null,\"b\":5}") assertThat(jsonAdapter.serializeNulls().toJson(encoded)).isEqualTo("{\"a\":null,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"b\":6}") val decoded = jsonAdapter.fromJson("{\"b\":6}")!!
assertThat(decoded.a).isNull() assertThat(decoded.a).isNull()
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -201,7 +201,7 @@ class KotlinJsonAdapterTest {
val encoded = ConstructorParameterWithQualifier("Android", "Banana") val encoded = ConstructorParameterWithQualifier("Android", "Banana")
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":\"ANDROID\",\"b\":\"Banana\"}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":\"ANDROID\",\"b\":\"Banana\"}")
val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}") val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}")!!
assertThat(decoded.a).isEqualTo("android") assertThat(decoded.a).isEqualTo("android")
assertThat(decoded.b).isEqualTo("Banana") assertThat(decoded.b).isEqualTo("Banana")
} }
@@ -220,7 +220,7 @@ class KotlinJsonAdapterTest {
encoded.b = "Banana" encoded.b = "Banana"
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":\"ANDROID\",\"b\":\"Banana\"}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":\"ANDROID\",\"b\":\"Banana\"}")
val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}") val decoded = jsonAdapter.fromJson("{\"a\":\"Android\",\"b\":\"Banana\"}")!!
assertThat(decoded.a).isEqualTo("android") assertThat(decoded.a).isEqualTo("android")
assertThat(decoded.b).isEqualTo("Banana") assertThat(decoded.b).isEqualTo("Banana")
} }
@@ -237,7 +237,7 @@ class KotlinJsonAdapterTest {
val encoded = ConstructorParameterWithJsonName(3, 5) val encoded = ConstructorParameterWithJsonName(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"key a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"key a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -253,7 +253,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5 encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"key a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"key a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"key a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -270,7 +270,7 @@ class KotlinJsonAdapterTest {
val encoded = TransientConstructorParameter(3, 5) val encoded = TransientConstructorParameter(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(-1) assertThat(decoded.a).isEqualTo(-1)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -286,7 +286,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5 encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(-1) assertThat(decoded.a).isEqualTo(-1)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -303,7 +303,7 @@ class KotlinJsonAdapterTest {
val encoded = SubtypeConstructorParameters(3, 5) val encoded = SubtypeConstructorParameters(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -321,7 +321,7 @@ class KotlinJsonAdapterTest {
encoded.b = 5 encoded.b = 5
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5,\"a\":3}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"b\":5,\"a\":3}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.b).isEqualTo(6) assertThat(decoded.b).isEqualTo(6)
} }
@@ -341,7 +341,7 @@ class KotlinJsonAdapterTest {
val encoded = ExtendsPlatformClassWithPrivateField(3) val encoded = ExtendsPlatformClassWithPrivateField(3)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"id\":\"B\"}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"id\":\"B\"}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.id).isEqualTo("C") assertThat(decoded.id).isEqualTo("C")
} }
@@ -355,7 +355,7 @@ class KotlinJsonAdapterTest {
val encoded = ExtendsPlatformClassWithProtectedField(3) val encoded = ExtendsPlatformClassWithProtectedField(3)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"buf\":[0,0],\"count\":0}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"buf\":[0,0],\"count\":0}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"buf\":[0,0],\"size\":0}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"buf\":[0,0],\"size\":0}")!!
assertThat(decoded.a).isEqualTo(4) assertThat(decoded.a).isEqualTo(4)
assertThat(decoded.buf()).isEqualTo(ByteArray(2, { 0 })) assertThat(decoded.buf()).isEqualTo(ByteArray(2, { 0 }))
assertThat(decoded.count()).isEqualTo(0) assertThat(decoded.count()).isEqualTo(0)
@@ -384,7 +384,7 @@ class KotlinJsonAdapterTest {
val encoded = PrivateConstructorParameters(3, 5) val encoded = PrivateConstructorParameters(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a()).isEqualTo(4) assertThat(decoded.a()).isEqualTo(4)
assertThat(decoded.b()).isEqualTo(6) assertThat(decoded.b()).isEqualTo(6)
} }
@@ -401,7 +401,7 @@ class KotlinJsonAdapterTest {
val encoded = PrivateConstructor.newInstance(3, 5) val encoded = PrivateConstructor.newInstance(3, 5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a()).isEqualTo(4) assertThat(decoded.a()).isEqualTo(4)
assertThat(decoded.b()).isEqualTo(6) assertThat(decoded.b()).isEqualTo(6)
} }
@@ -423,7 +423,7 @@ class KotlinJsonAdapterTest {
encoded.b(5) encoded.b(5)
assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}") assertThat(jsonAdapter.toJson(encoded)).isEqualTo("{\"a\":3,\"b\":5}")
val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}") val decoded = jsonAdapter.fromJson("{\"a\":4,\"b\":6}")!!
assertThat(decoded.a()).isEqualTo(4) assertThat(decoded.a()).isEqualTo(4)
assertThat(decoded.b()).isEqualTo(6) assertThat(decoded.b()).isEqualTo(6)
} }

View File

@@ -17,6 +17,11 @@
<groupId>com.squareup.okio</groupId> <groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId> <artifactId>okio</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>

View File

@@ -24,6 +24,7 @@ import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
import static com.squareup.moshi.Util.jsonAnnotations; import static com.squareup.moshi.Util.jsonAnnotations;
@@ -36,7 +37,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
this.fromAdapters = fromAdapters; this.fromAdapters = fromAdapters;
} }
@Override public JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
final Type type, final Set<? extends Annotation> annotations, final Moshi moshi) { final Type type, final Set<? extends Annotation> annotations, final Moshi moshi) {
final AdapterMethod toAdapter = get(toAdapters, type, annotations); final AdapterMethod toAdapter = get(toAdapters, type, annotations);
final AdapterMethod fromAdapter = get(fromAdapters, type, annotations); final AdapterMethod fromAdapter = get(fromAdapters, type, annotations);
@@ -59,7 +60,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
if (fromAdapter != null) fromAdapter.bind(moshi, this); if (fromAdapter != null) fromAdapter.bind(moshi, this);
return new JsonAdapter<Object>() { return new JsonAdapter<Object>() {
@Override public void toJson(JsonWriter writer, Object value) throws IOException { @Override public void toJson(JsonWriter writer, @Nullable Object value) throws IOException {
if (toAdapter == null) { if (toAdapter == null) {
delegate.toJson(writer, value); delegate.toJson(writer, value);
} else if (!toAdapter.nullable && value == null) { } else if (!toAdapter.nullable && value == null) {
@@ -75,7 +76,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
} }
} }
@Override public Object fromJson(JsonReader reader) throws IOException { @Override public @Nullable 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) { } else if (!fromAdapter.nullable && reader.peek() == JsonReader.Token.NULL) {
@@ -155,7 +156,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
Set<? extends Annotation> qualifierAnnotations = jsonAnnotations(parameterAnnotations[1]); Set<? extends Annotation> qualifierAnnotations = jsonAnnotations(parameterAnnotations[1]);
return new AdapterMethod(parameterTypes[1], qualifierAnnotations, adapter, method, return new AdapterMethod(parameterTypes[1], qualifierAnnotations, adapter, method,
parameterTypes.length, 2, true) { parameterTypes.length, 2, true) {
@Override public void toJson(Moshi moshi, JsonWriter writer, Object value) @Override public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
throws IOException, InvocationTargetException { throws IOException, InvocationTargetException {
invoke(writer, value); invoke(writer, value);
} }
@@ -175,7 +176,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
delegate = moshi.adapter(returnType, returnTypeAnnotations); delegate = moshi.adapter(returnType, returnTypeAnnotations);
} }
@Override public void toJson(Moshi moshi, JsonWriter writer, Object value) @Override public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
throws IOException, InvocationTargetException { throws IOException, InvocationTargetException {
Object intermediate = invoke(value); Object intermediate = invoke(value);
delegate.toJson(writer, intermediate); delegate.toJson(writer, intermediate);
@@ -260,7 +261,7 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
} }
/** Returns the matching adapter method from the list. */ /** Returns the matching adapter method from the list. */
private static AdapterMethod get( private static @Nullable AdapterMethod get(
List<AdapterMethod> adapterMethods, Type type, Set<? extends Annotation> annotations) { List<AdapterMethod> adapterMethods, Type type, Set<? extends Annotation> annotations) {
for (int i = 0, size = adapterMethods.size(); i < size; i++) { for (int i = 0, size = adapterMethods.size(); i < size; i++) {
AdapterMethod adapterMethod = adapterMethods.get(i); AdapterMethod adapterMethod = adapterMethods.get(i);
@@ -306,18 +307,18 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
} }
} }
public void toJson(Moshi moshi, JsonWriter writer, Object value) public void toJson(Moshi moshi, JsonWriter writer, @Nullable Object value)
throws IOException, InvocationTargetException { throws IOException, InvocationTargetException {
throw new AssertionError(); throw new AssertionError();
} }
public Object fromJson(Moshi moshi, JsonReader reader) public @Nullable Object fromJson(Moshi moshi, JsonReader reader)
throws IOException, InvocationTargetException { throws IOException, InvocationTargetException {
throw new AssertionError(); throw new AssertionError();
} }
/** Invoke the method with one fixed argument, plus any number of JSON adapter arguments. */ /** Invoke the method with one fixed argument, plus any number of JSON adapter arguments. */
protected Object invoke(Object a1) throws InvocationTargetException { protected @Nullable Object invoke(@Nullable Object a1) throws InvocationTargetException {
Object[] args = new Object[1 + jsonAdapters.length]; Object[] args = new Object[1 + jsonAdapters.length];
args[0] = a1; args[0] = a1;
System.arraycopy(jsonAdapters, 0, args, 1, jsonAdapters.length); System.arraycopy(jsonAdapters, 0, args, 1, jsonAdapters.length);
@@ -330,7 +331,8 @@ final class AdapterMethodsFactory implements JsonAdapter.Factory {
} }
/** Invoke the method with two fixed arguments, plus any number of JSON adapter arguments. */ /** Invoke the method with two fixed arguments, plus any number of JSON adapter arguments. */
protected Object invoke(Object a1, Object a2) throws InvocationTargetException { protected Object invoke(@Nullable Object a1, @Nullable Object a2)
throws InvocationTargetException {
Object[] args = new Object[2 + jsonAdapters.length]; Object[] args = new Object[2 + jsonAdapters.length];
args[0] = a1; args[0] = a1;
args[1] = a2; args[1] = a2;

View File

@@ -22,6 +22,7 @@ import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
/** /**
* Converts arrays to JSON arrays containing their converted contents. This * Converts arrays to JSON arrays containing their converted contents. This
@@ -29,7 +30,7 @@ import java.util.Set;
*/ */
final class ArrayJsonAdapter extends JsonAdapter<Object> { final class ArrayJsonAdapter extends JsonAdapter<Object> {
public static final Factory FACTORY = new Factory() { public static final Factory FACTORY = new Factory() {
@Override public JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) { Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Type elementType = Types.arrayComponentType(type); Type elementType = Types.arrayComponentType(type);
if (elementType == null) return null; if (elementType == null) return null;

View File

@@ -24,6 +24,7 @@ import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeMap; import java.util.TreeMap;
import javax.annotation.Nullable;
/** /**
* Emits a regular class as a JSON object by mapping Java fields to JSON object properties. * Emits a regular class as a JSON object by mapping Java fields to JSON object properties.
@@ -42,7 +43,7 @@ import java.util.TreeMap;
*/ */
final class ClassJsonAdapter<T> extends JsonAdapter<T> { final class ClassJsonAdapter<T> extends JsonAdapter<T> {
public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() { public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) { Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Class<?> rawType = Types.getRawType(type); Class<?> rawType = Types.getRawType(type);
if (rawType.isInterface() || rawType.isEnum()) return null; if (rawType.isInterface() || rawType.isEnum()) return null;

View File

@@ -23,11 +23,12 @@ import java.util.Collection;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
/** Converts collection types to JSON arrays containing their converted contents. */ /** Converts collection types to JSON arrays containing their converted contents. */
abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAdapter<C> { abstract class CollectionJsonAdapter<C extends Collection<T>, T> extends JsonAdapter<C> {
public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() { public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) { Type type, Set<? extends Annotation> annotations, Moshi moshi) {
Class<?> rawType = Types.getRawType(type); Class<?> rawType = Types.getRawType(type);
if (!annotations.isEmpty()) return null; if (!annotations.isEmpty()) return null;

View File

@@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
import okio.Buffer; import okio.Buffer;
import okio.BufferedSink; import okio.BufferedSink;
import okio.BufferedSource; import okio.BufferedSource;
@@ -28,24 +29,24 @@ import okio.BufferedSource;
* Converts Java values to JSON, and JSON values to Java. * Converts Java values to JSON, and JSON values to Java.
*/ */
public abstract class JsonAdapter<T> { public abstract class JsonAdapter<T> {
public abstract T fromJson(JsonReader reader) throws IOException; public abstract @Nullable T fromJson(JsonReader reader) throws IOException;
public final T fromJson(BufferedSource source) throws IOException { public final @Nullable T fromJson(BufferedSource source) throws IOException {
return fromJson(JsonReader.of(source)); return fromJson(JsonReader.of(source));
} }
public final T fromJson(String string) throws IOException { public final @Nullable T fromJson(String string) throws IOException {
return fromJson(new Buffer().writeUtf8(string)); return fromJson(new Buffer().writeUtf8(string));
} }
public abstract void toJson(JsonWriter writer, T value) throws IOException; public abstract void toJson(JsonWriter writer, @Nullable T value) throws IOException;
public final void toJson(BufferedSink sink, T value) throws IOException { public final void toJson(BufferedSink sink, @Nullable T value) throws IOException {
JsonWriter writer = JsonWriter.of(sink); JsonWriter writer = JsonWriter.of(sink);
toJson(writer, value); toJson(writer, value);
} }
public final String toJson(T value) { public final String toJson(@Nullable T value) {
Buffer buffer = new Buffer(); Buffer buffer = new Buffer();
try { try {
toJson(buffer, value); toJson(buffer, value);
@@ -65,7 +66,7 @@ public abstract class JsonAdapter<T> {
* Long}), as a {@link Double} for boxed floating point types ({@link Float} and {@link Double}), * Long}), as a {@link Double} for boxed floating point types ({@link Float} and {@link Double}),
* and as a {@link BigDecimal} for all other types. * and as a {@link BigDecimal} for all other types.
*/ */
public final Object toJsonValue(T value) { public final @Nullable Object toJsonValue(@Nullable T value) {
JsonValueWriter writer = new JsonValueWriter(); JsonValueWriter writer = new JsonValueWriter();
try { try {
toJson(writer, value); toJson(writer, value);
@@ -79,7 +80,7 @@ public abstract class JsonAdapter<T> {
* Decodes a Java value object from {@code value}, which must be comprised of maps, lists, * Decodes a Java value object from {@code value}, which must be comprised of maps, lists,
* strings, numbers, booleans and nulls. * strings, numbers, booleans and nulls.
*/ */
public final T fromJsonValue(Object value) { public final @Nullable T fromJsonValue(@Nullable Object value) {
JsonValueReader reader = new JsonValueReader(value); JsonValueReader reader = new JsonValueReader(value);
try { try {
return fromJson(reader); return fromJson(reader);
@@ -95,10 +96,10 @@ public abstract class JsonAdapter<T> {
public final JsonAdapter<T> serializeNulls() { public final JsonAdapter<T> serializeNulls() {
final JsonAdapter<T> delegate = this; final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() { return new JsonAdapter<T>() {
@Override public T fromJson(JsonReader reader) throws IOException { @Override public @Nullable T fromJson(JsonReader reader) throws IOException {
return delegate.fromJson(reader); return delegate.fromJson(reader);
} }
@Override public void toJson(JsonWriter writer, T value) throws IOException { @Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
boolean serializeNulls = writer.getSerializeNulls(); boolean serializeNulls = writer.getSerializeNulls();
writer.setSerializeNulls(true); writer.setSerializeNulls(true);
try { try {
@@ -120,14 +121,14 @@ public abstract class JsonAdapter<T> {
public final JsonAdapter<T> nullSafe() { public final JsonAdapter<T> nullSafe() {
final JsonAdapter<T> delegate = this; final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() { return new JsonAdapter<T>() {
@Override public T fromJson(JsonReader reader) throws IOException { @Override public @Nullable T fromJson(JsonReader reader) throws IOException {
if (reader.peek() == JsonReader.Token.NULL) { if (reader.peek() == JsonReader.Token.NULL) {
return reader.nextNull(); return reader.nextNull();
} else { } else {
return delegate.fromJson(reader); return delegate.fromJson(reader);
} }
} }
@Override public void toJson(JsonWriter writer, T value) throws IOException { @Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
if (value == null) { if (value == null) {
writer.nullValue(); writer.nullValue();
} else { } else {
@@ -144,7 +145,7 @@ public abstract class JsonAdapter<T> {
public final JsonAdapter<T> lenient() { public final JsonAdapter<T> lenient() {
final JsonAdapter<T> delegate = this; final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() { return new JsonAdapter<T>() {
@Override public T fromJson(JsonReader reader) throws IOException { @Override public @Nullable T fromJson(JsonReader reader) throws IOException {
boolean lenient = reader.isLenient(); boolean lenient = reader.isLenient();
reader.setLenient(true); reader.setLenient(true);
try { try {
@@ -153,7 +154,7 @@ public abstract class JsonAdapter<T> {
reader.setLenient(lenient); reader.setLenient(lenient);
} }
} }
@Override public void toJson(JsonWriter writer, T value) throws IOException { @Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
boolean lenient = writer.isLenient(); boolean lenient = writer.isLenient();
writer.setLenient(true); writer.setLenient(true);
try { try {
@@ -177,7 +178,7 @@ public abstract class JsonAdapter<T> {
public final JsonAdapter<T> failOnUnknown() { public final JsonAdapter<T> failOnUnknown() {
final JsonAdapter<T> delegate = this; final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() { return new JsonAdapter<T>() {
@Override public T fromJson(JsonReader reader) throws IOException { @Override public @Nullable T fromJson(JsonReader reader) throws IOException {
boolean skipForbidden = reader.failOnUnknown(); boolean skipForbidden = reader.failOnUnknown();
reader.setFailOnUnknown(true); reader.setFailOnUnknown(true);
try { try {
@@ -186,7 +187,7 @@ public abstract class JsonAdapter<T> {
reader.setFailOnUnknown(skipForbidden); reader.setFailOnUnknown(skipForbidden);
} }
} }
@Override public void toJson(JsonWriter writer, T value) throws IOException { @Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
delegate.toJson(writer, value); delegate.toJson(writer, value);
} }
@Override public String toString() { @Override public String toString() {
@@ -209,10 +210,10 @@ public abstract class JsonAdapter<T> {
} }
final JsonAdapter<T> delegate = this; final JsonAdapter<T> delegate = this;
return new JsonAdapter<T>() { return new JsonAdapter<T>() {
@Override public T fromJson(JsonReader reader) throws IOException { @Override public @Nullable T fromJson(JsonReader reader) throws IOException {
return delegate.fromJson(reader); return delegate.fromJson(reader);
} }
@Override public void toJson(JsonWriter writer, T value) throws IOException { @Override public void toJson(JsonWriter writer, @Nullable T value) throws IOException {
String originalIndent = writer.getIndent(); String originalIndent = writer.getIndent();
writer.setIndent(indent); writer.setIndent(indent);
try { try {
@@ -236,6 +237,6 @@ public abstract class JsonAdapter<T> {
* <p>Implementations may use to {@link Moshi#adapter} to compose adapters of other types, or * <p>Implementations may use to {@link Moshi#adapter} to compose adapters of other types, or
* {@link Moshi#nextAdapter} to delegate to the underlying adapter of the same type. * {@link Moshi#nextAdapter} to delegate to the underlying adapter of the same type.
*/ */
JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi); @Nullable JsonAdapter<?> create(Type type, Set<? extends Annotation> annotations, Moshi moshi);
} }
} }

View File

@@ -15,6 +15,8 @@
*/ */
package com.squareup.moshi; package com.squareup.moshi;
import javax.annotation.Nullable;
/** /**
* Thrown when the data in a JSON document doesn't match the data expected by the caller. For * Thrown when the data in a JSON document doesn't match the data expected by the caller. For
* example, suppose the application expects a boolean but the JSON document contains a string. When * example, suppose the application expects a boolean but the JSON document contains a string. When
@@ -31,15 +33,15 @@ public final class JsonDataException extends RuntimeException {
public JsonDataException() { public JsonDataException() {
} }
public JsonDataException(String message) { public JsonDataException(@Nullable String message) {
super(message); super(message);
} }
public JsonDataException(Throwable cause) { public JsonDataException(@Nullable Throwable cause) {
super(cause); super(cause);
} }
public JsonDataException(String message, Throwable cause) { public JsonDataException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause); super(message, cause);
} }
} }

View File

@@ -16,10 +16,11 @@
package com.squareup.moshi; package com.squareup.moshi;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nullable;
/** Thrown when the data being parsed is not encoded as valid JSON. */ /** Thrown when the data being parsed is not encoded as valid JSON. */
public final class JsonEncodingException extends IOException { public final class JsonEncodingException extends IOException {
public JsonEncodingException(String message) { public JsonEncodingException(@Nullable String message) {
super(message); super(message);
} }
} }

View File

@@ -20,6 +20,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable;
import okio.Buffer; import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
@@ -212,7 +213,7 @@ public abstract class JsonReader implements Closeable {
throw new JsonEncodingException(message + " at path " + getPath()); throw new JsonEncodingException(message + " at path " + getPath());
} }
final JsonDataException typeMismatch(Object value, Object expected) { final JsonDataException typeMismatch(@Nullable Object value, Object expected) {
if (value == null) { if (value == null) {
return new JsonDataException( return new JsonDataException(
"Expected " + expected + " but was null at path " + getPath()); "Expected " + expected + " but was null at path " + getPath());
@@ -351,7 +352,7 @@ public abstract class JsonReader implements Closeable {
* *
* @throws JsonDataException if the next token is not null or if this reader is closed. * @throws JsonDataException if the next token is not null or if this reader is closed.
*/ */
public abstract <T> T nextNull() throws IOException; public abstract @Nullable <T> T nextNull() throws IOException;
/** /**
* Returns the {@linkplain Token#NUMBER double} value of the next token, consuming it. If the next * Returns the {@linkplain Token#NUMBER double} value of the next token, consuming it. If the next
@@ -400,7 +401,7 @@ public abstract class JsonReader implements Closeable {
* @throws JsonDataException if the next token is not a literal value, if a JSON object has a * @throws JsonDataException if the next token is not a literal value, if a JSON object has a
* duplicate key. * duplicate key.
*/ */
public final Object readJsonValue() throws IOException { public final @Nullable Object readJsonValue() throws IOException {
switch (peek()) { switch (peek()) {
case BEGIN_ARRAY: case BEGIN_ARRAY:
List<Object> list = new ArrayList<>(); List<Object> list = new ArrayList<>();

View File

@@ -18,6 +18,7 @@ package com.squareup.moshi;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.math.BigDecimal; import java.math.BigDecimal;
import javax.annotation.Nullable;
import okio.Buffer; import okio.Buffer;
import okio.BufferedSource; import okio.BufferedSource;
import okio.ByteString; import okio.ByteString;
@@ -86,7 +87,7 @@ final class JsonUtf8Reader extends JsonReader {
* This is populated before a numeric value is parsed and used if that parsing * This is populated before a numeric value is parsed and used if that parsing
* fails. * fails.
*/ */
private String peekedString; private @Nullable String peekedString;
JsonUtf8Reader(BufferedSource source) { JsonUtf8Reader(BufferedSource source) {
if (source == null) { if (source == null) {
@@ -670,7 +671,7 @@ final class JsonUtf8Reader extends JsonReader {
throw new JsonDataException("Expected a boolean but was " + peek() + " at path " + getPath()); throw new JsonDataException("Expected a boolean but was " + peek() + " at path " + getPath());
} }
@Override public <T> T nextNull() throws IOException { @Override public @Nullable <T> T nextNull() throws IOException {
int p = peeked; int p = peeked;
if (p == PEEKED_NONE) { if (p == PEEKED_NONE) {
p = doPeek(); p = doPeek();

View File

@@ -16,6 +16,7 @@
package com.squareup.moshi; package com.squareup.moshi;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nullable;
import okio.BufferedSink; import okio.BufferedSink;
import okio.Sink; import okio.Sink;
@@ -222,7 +223,7 @@ final class JsonUtf8Writer extends JsonWriter {
return this; return this;
} }
@Override public JsonWriter value(Number value) throws IOException { @Override public JsonWriter value(@Nullable Number value) throws IOException {
if (value == null) { if (value == null) {
return nullValue(); return nullValue();
} }

View File

@@ -22,6 +22,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable;
/** /**
* This class reads a JSON document by traversing a Java object comprising maps, lists, and JSON * This class reads a JSON document by traversing a Java object comprising maps, lists, and JSON
@@ -173,7 +174,7 @@ final class JsonValueReader extends JsonReader {
return peeked; return peeked;
} }
@Override public <T> T nextNull() throws IOException { @Override public @Nullable <T> T nextNull() throws IOException {
require(Void.class, Token.NULL); require(Void.class, Token.NULL);
remove(); remove();
return null; return null;
@@ -297,7 +298,7 @@ final class JsonValueReader extends JsonReader {
* Returns the top of the stack which is required to be a {@code type}. Throws if this reader is * Returns the top of the stack which is required to be a {@code type}. Throws if this reader is
* closed, or if the type isn't what was expected. * closed, or if the type isn't what was expected.
*/ */
private <T> T require(Class<T> type, Token expected) throws IOException { private @Nullable <T> T require(Class<T> type, Token expected) throws IOException {
Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null); Object peeked = (stackSize != 0 ? stack[stackSize - 1] : null);
if (type.isInstance(peeked)) { if (type.isInstance(peeked)) {

View File

@@ -20,6 +20,7 @@ import java.math.BigDecimal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nullable;
import static com.squareup.moshi.JsonScope.EMPTY_ARRAY; import static com.squareup.moshi.JsonScope.EMPTY_ARRAY;
import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT; import static com.squareup.moshi.JsonScope.EMPTY_DOCUMENT;
@@ -31,7 +32,7 @@ import static java.lang.Double.POSITIVE_INFINITY;
/** Writes JSON by building a Java object comprising maps, lists, and JSON primitives. */ /** Writes JSON by building a Java object comprising maps, lists, and JSON primitives. */
final class JsonValueWriter extends JsonWriter { final class JsonValueWriter extends JsonWriter {
private final Object[] stack = new Object[32]; private final Object[] stack = new Object[32];
private String deferredName; private @Nullable String deferredName;
JsonValueWriter() { JsonValueWriter() {
pushScope(EMPTY_DOCUMENT); pushScope(EMPTY_DOCUMENT);
@@ -106,7 +107,7 @@ final class JsonValueWriter extends JsonWriter {
return this; return this;
} }
@Override public JsonWriter value(String value) throws IOException { @Override public JsonWriter value(@Nullable String value) throws IOException {
if (promoteValueToName) { if (promoteValueToName) {
return name(value); return name(value);
} }
@@ -127,7 +128,7 @@ final class JsonValueWriter extends JsonWriter {
return this; return this;
} }
@Override public JsonWriter value(Boolean value) throws IOException { @Override public JsonWriter value(@Nullable Boolean value) throws IOException {
add(value); add(value);
pathIndices[stackSize - 1]++; pathIndices[stackSize - 1]++;
return this; return this;
@@ -155,7 +156,7 @@ final class JsonValueWriter extends JsonWriter {
return this; return this;
} }
@Override public JsonWriter value(Number value) throws IOException { @Override public JsonWriter value(@Nullable Number value) throws IOException {
// If it's trivially converted to a long, do that. // If it's trivially converted to a long, do that.
if (value instanceof Byte if (value instanceof Byte
|| value instanceof Short || value instanceof Short
@@ -169,6 +170,10 @@ final class JsonValueWriter extends JsonWriter {
return value(value.doubleValue()); return value(value.doubleValue());
} }
if (value == null) {
return nullValue();
}
// Everything else gets converted to a BigDecimal. // Everything else gets converted to a BigDecimal.
BigDecimal bigDecimalValue = value instanceof BigDecimal BigDecimal bigDecimalValue = value instanceof BigDecimal
? ((BigDecimal) value) ? ((BigDecimal) value)
@@ -195,7 +200,7 @@ final class JsonValueWriter extends JsonWriter {
} }
} }
private JsonValueWriter add(Object newTop) { private JsonValueWriter add(@Nullable Object newTop) {
int scope = peekScope(); int scope = peekScope();
if (stackSize == 1) { if (stackSize == 1) {

View File

@@ -18,6 +18,7 @@ package com.squareup.moshi;
import java.io.Closeable; import java.io.Closeable;
import java.io.Flushable; import java.io.Flushable;
import java.io.IOException; import java.io.IOException;
import javax.annotation.Nullable;
import okio.BufferedSink; import okio.BufferedSink;
import static com.squareup.moshi.JsonScope.EMPTY_OBJECT; import static com.squareup.moshi.JsonScope.EMPTY_OBJECT;
@@ -257,7 +258,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
/** /**
* Encodes the property name. * Encodes the property name.
* *
* @param name the name of the forthcoming value. May not be null. * @param name the name of the forthcoming value. Must not be null.
* @return this writer. * @return this writer.
*/ */
public abstract JsonWriter name(String name) throws IOException; public abstract JsonWriter name(String name) throws IOException;
@@ -268,7 +269,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
* @param value the literal string value, or null to encode a null literal. * @param value the literal string value, or null to encode a null literal.
* @return this writer. * @return this writer.
*/ */
public abstract JsonWriter value(String value) throws IOException; public abstract JsonWriter value(@Nullable String value) throws IOException;
/** /**
* Encodes {@code null}. * Encodes {@code null}.
@@ -289,7 +290,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
* *
* @return this writer. * @return this writer.
*/ */
public abstract JsonWriter value(Boolean value) throws IOException; public abstract JsonWriter value(@Nullable Boolean value) throws IOException;
/** /**
* Encodes {@code value}. * Encodes {@code value}.
@@ -314,7 +315,7 @@ public abstract class JsonWriter implements Closeable, Flushable {
* {@linkplain Double#isInfinite() infinities}. * {@linkplain Double#isInfinite() infinities}.
* @return this writer. * @return this writer.
*/ */
public abstract JsonWriter value(Number value) throws IOException; public abstract JsonWriter value(@Nullable Number value) throws IOException;
/** /**
* Changes the writer to treat the next value as a string name. This is useful for map adapters so * Changes the writer to treat the next value as a string name. This is useful for map adapters so

View File

@@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
/** /**
* Converts maps with string keys to JSON objects. * Converts maps with string keys to JSON objects.
@@ -28,7 +29,7 @@ import java.util.Set;
*/ */
final class MapJsonAdapter<K, V> extends JsonAdapter<Map<K, V>> { final class MapJsonAdapter<K, V> extends JsonAdapter<Map<K, V>> {
public static final Factory FACTORY = new Factory() { public static final Factory FACTORY = new Factory() {
@Override public JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
Type type, Set<? extends Annotation> annotations, Moshi moshi) { Type type, Set<? extends Annotation> annotations, Moshi moshi) {
if (!annotations.isEmpty()) return null; if (!annotations.isEmpty()) return null;
Class<?> rawType = Types.getRawType(type); Class<?> rawType = Types.getRawType(type);

View File

@@ -25,6 +25,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
/** /**
* Coordinates binding between JSON values and Java objects. * Coordinates binding between JSON values and Java objects.
@@ -154,7 +155,7 @@ public final class Moshi {
if (jsonAdapter == null) throw new IllegalArgumentException("jsonAdapter == null"); if (jsonAdapter == null) throw new IllegalArgumentException("jsonAdapter == null");
return add(new JsonAdapter.Factory() { return add(new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
Type targetType, Set<? extends Annotation> annotations, Moshi moshi) { Type targetType, Set<? extends Annotation> annotations, Moshi moshi) {
return annotations.isEmpty() && Util.typesMatch(type, targetType) ? jsonAdapter : null; return annotations.isEmpty() && Util.typesMatch(type, targetType) ? jsonAdapter : null;
} }
@@ -174,7 +175,7 @@ public final class Moshi {
} }
return add(new JsonAdapter.Factory() { return add(new JsonAdapter.Factory() {
@Override public JsonAdapter<?> create( @Override public @Nullable JsonAdapter<?> create(
Type targetType, Set<? extends Annotation> annotations, Moshi moshi) { Type targetType, Set<? extends Annotation> annotations, Moshi moshi) {
if (Util.typesMatch(type, targetType) if (Util.typesMatch(type, targetType)
&& annotations.size() == 1 && annotations.size() == 1
@@ -216,8 +217,8 @@ public final class Moshi {
* class that has a {@code List<Employee>} field for an organization's management hierarchy. * class that has a {@code List<Employee>} field for an organization's management hierarchy.
*/ */
private static class DeferredAdapter<T> extends JsonAdapter<T> { private static class DeferredAdapter<T> extends JsonAdapter<T> {
Object cacheKey; @Nullable Object cacheKey;
private JsonAdapter<T> delegate; private @Nullable JsonAdapter<T> delegate;
DeferredAdapter(Object cacheKey) { DeferredAdapter(Object cacheKey) {
this.cacheKey = cacheKey; this.cacheKey = cacheKey;

View File

@@ -34,6 +34,7 @@ import java.util.Map;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
/** Factory methods for types. */ /** Factory methods for types. */
public final class Types { public final class Types {
@@ -200,12 +201,12 @@ public final class Types {
}); });
} }
static boolean equal(Object a, Object b) { static boolean equal(@Nullable Object a, @Nullable Object b) {
return a == b || (a != null && a.equals(b)); return a == b || (a != null && a.equals(b));
} }
/** Returns true if {@code a} and {@code b} are equal. */ /** Returns true if {@code a} and {@code b} are equal. */
public static boolean equals(Type a, Type b) { public static boolean equals(@Nullable Type a, @Nullable Type b) {
if (a == b) { if (a == b) {
return true; // Also handles (a == null && b == null). return true; // Also handles (a == null && b == null).
@@ -260,7 +261,7 @@ public final class Types {
} }
} }
static int hashCodeOrZero(Object o) { static int hashCodeOrZero(@Nullable Object o) {
return o != null ? o.hashCode() : 0; return o != null ? o.hashCode() : 0;
} }
@@ -484,7 +485,7 @@ public final class Types {
* Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by * Returns the declaring class of {@code typeVariable}, or {@code null} if it was not declared by
* a class. * a class.
*/ */
private static Class<?> declaringClassOf(TypeVariable<?> typeVariable) { private static @Nullable Class<?> declaringClassOf(TypeVariable<?> typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null; return genericDeclaration instanceof Class ? (Class<?>) genericDeclaration : null;
} }
@@ -496,11 +497,11 @@ public final class Types {
} }
private static final class ParameterizedTypeImpl implements ParameterizedType { private static final class ParameterizedTypeImpl implements ParameterizedType {
private final Type ownerType; private final @Nullable Type ownerType;
private final Type rawType; private final Type rawType;
final Type[] typeArguments; final Type[] typeArguments;
ParameterizedTypeImpl(Type ownerType, Type rawType, Type... typeArguments) { ParameterizedTypeImpl(@Nullable Type ownerType, Type rawType, Type... typeArguments) {
// Require an owner type if the raw type needs it. // Require an owner type if the raw type needs it.
if (rawType instanceof Class<?> if (rawType instanceof Class<?>
&& (ownerType == null) != (((Class<?>) rawType).getEnclosingClass() == null)) { && (ownerType == null) != (((Class<?>) rawType).getEnclosingClass() == null)) {
@@ -526,7 +527,7 @@ public final class Types {
return rawType; return rawType;
} }
@Override public Type getOwnerType() { @Override public @Nullable Type getOwnerType() {
return ownerType; return ownerType;
} }
@@ -589,7 +590,7 @@ public final class Types {
*/ */
private static final class WildcardTypeImpl implements WildcardType { private static final class WildcardTypeImpl implements WildcardType {
private final Type upperBound; private final Type upperBound;
private final Type lowerBound; private final @Nullable Type lowerBound;
WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) { WildcardTypeImpl(Type[] upperBounds, Type[] lowerBounds) {
if (lowerBounds.length > 1) throw new IllegalArgumentException(); if (lowerBounds.length > 1) throw new IllegalArgumentException();

View File

@@ -0,0 +1,3 @@
/** Moshi is modern JSON library for Android and Java. */
@javax.annotation.ParametersAreNonnullByDefault
package com.squareup.moshi;

View File

@@ -286,6 +286,15 @@ public final class JsonWriterTest {
+ "3.141592653589793238462643383]"); + "3.141592653589793238462643383]");
} }
@Test public void nullNumbers() throws IOException {
JsonWriter writer = factory.newWriter();
writer.beginArray();
writer.value((Number) null);
writer.endArray();
writer.close();
assertThat(factory.json()).isEqualTo("[null]");
}
@Test public void booleans() throws IOException { @Test public void booleans() throws IOException {
JsonWriter writer = factory.newWriter(); JsonWriter writer = factory.newWriter();
writer.beginArray(); writer.beginArray();

View File

@@ -58,6 +58,12 @@
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>