Skip to content

Commit

Permalink
Fail gracefully when a primitive value is absent. (#1445)
Browse files Browse the repository at this point in the history
Without this we get an AssertionError, which is the wrong exception
type for a JSON schema mismatch:

    java.lang.AssertionError: java.lang.NullPointerException: Cannot invoke "java.lang.Number.intValue()" because the return value of "sun.invoke.util.ValueConversions.primitiveConversion(sun.invoke.util.Wrapper, Object, boolean)" is null
      at com.squareup.moshi.RecordJsonAdapter.fromJson(RecordJsonAdapter.java:168)
      at com.squareup.moshi.internal.NullSafeJsonAdapter.fromJson(NullSafeJsonAdapter.java:41)
      at com.squareup.moshi.JsonAdapter.fromJson(JsonAdapter.java:70)
      at com.squareup.moshi.records.RecordsTest.absentPrimitiveFails(RecordsTest.java:257)
    Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.Number.intValue()" because the return value of "sun.invoke.util.ValueConversions.primitiveConversion(sun.invoke.util.Wrapper, Object, boolean)" is null
      at java.base/sun.invoke.util.ValueConversions.unboxInteger(ValueConversions.java:81)
      at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:732)
      at com.squareup.moshi.RecordJsonAdapter.fromJson(RecordJsonAdapter.java:156)
      ... 46 more
  • Loading branch information
swankjesse committed Dec 5, 2021
1 parent 75abba3 commit 3cbd5c3
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 0 deletions.
Expand Up @@ -21,6 +21,7 @@

import com.squareup.moshi.FromJson;
import com.squareup.moshi.Json;
import com.squareup.moshi.JsonDataException;
import com.squareup.moshi.JsonQualifier;
import com.squareup.moshi.JsonReader;
import com.squareup.moshi.JsonWriter;
Expand Down Expand Up @@ -248,4 +249,46 @@ boolean booleanFromJson(JsonReader reader) throws IOException {
}

public static record BooleanRecord(boolean value) {}

@Test
public void absentPrimitiveFails() throws IOException {
var adapter = moshi.adapter(AbsentValues.class);
try {
adapter.fromJson("{\"s\":\"\"}");
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessageThat().isEqualTo("Required value 'i' missing at $");
}
}

@Test
public void nullPrimitiveFails() throws IOException {
var adapter = moshi.adapter(AbsentValues.class);
try {
adapter.fromJson("{\"s\":\"\",\"i\":null}");
fail();
} catch (JsonDataException expected) {
assertThat(expected).hasMessageThat().isEqualTo("Expected an int but was NULL at path $.i");
}
}

@Test
public void absentObjectIsNull() throws IOException {
var adapter = moshi.adapter(AbsentValues.class);
String json = "{\"i\":5}";
AbsentValues value = new AbsentValues(null, 5);
assertThat(adapter.fromJson(json)).isEqualTo(value);
assertThat(adapter.toJson(value)).isEqualTo(json);
}

@Test
public void nullObjectIsNull() throws IOException {
var adapter = moshi.adapter(AbsentValues.class);
String json = "{\"i\":5,\"s\":null}";
AbsentValues value = new AbsentValues(null, 5);
assertThat(adapter.fromJson(json)).isEqualTo(value);
assertThat(adapter.toJson(value)).isEqualTo("{\"i\":5}");
}

public static record AbsentValues(String s, int i) {}
}
Expand Up @@ -157,6 +157,14 @@ public T fromJson(JsonReader reader) throws IOException {
} catch (InvocationTargetException e) {
throw rethrowCause(e);
} catch (Throwable e) {
// Don't throw a fatal error if it's just an absent primitive.
for (int i = 0, limit = componentBindingsArray.length; i < limit; i++) {
if (resultsArray[i] == null
&& componentBindingsArray[i].accessor.type().returnType().isPrimitive()) {
throw Util.missingProperty(
componentBindingsArray[i].componentName, componentBindingsArray[i].jsonName, reader);
}
}
throw new AssertionError(e);
}
}
Expand Down

0 comments on commit 3cbd5c3

Please sign in to comment.