mirror of
https://github.com/fankes/moshi.git
synced 2025-10-19 07:59:21 +08:00
Fail gracefully when a primitive value is absent. (#1445)
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
This commit is contained in:
@@ -21,6 +21,7 @@ import static org.junit.Assert.fail;
|
|||||||
|
|
||||||
import com.squareup.moshi.FromJson;
|
import com.squareup.moshi.FromJson;
|
||||||
import com.squareup.moshi.Json;
|
import com.squareup.moshi.Json;
|
||||||
|
import com.squareup.moshi.JsonDataException;
|
||||||
import com.squareup.moshi.JsonQualifier;
|
import com.squareup.moshi.JsonQualifier;
|
||||||
import com.squareup.moshi.JsonReader;
|
import com.squareup.moshi.JsonReader;
|
||||||
import com.squareup.moshi.JsonWriter;
|
import com.squareup.moshi.JsonWriter;
|
||||||
@@ -248,4 +249,46 @@ public final class RecordsTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static record BooleanRecord(boolean value) {}
|
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) {}
|
||||||
}
|
}
|
||||||
|
@@ -157,6 +157,14 @@ final class RecordJsonAdapter<T> extends JsonAdapter<T> {
|
|||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
throw rethrowCause(e);
|
throw rethrowCause(e);
|
||||||
} catch (Throwable 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);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user