diff --git a/moshi/records-tests/src/test/java/com/squareup/moshi/records/RecordsTest.java b/moshi/records-tests/src/test/java/com/squareup/moshi/records/RecordsTest.java index f2b33fedd..5c814ac89 100644 --- a/moshi/records-tests/src/test/java/com/squareup/moshi/records/RecordsTest.java +++ b/moshi/records-tests/src/test/java/com/squareup/moshi/records/RecordsTest.java @@ -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; @@ -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) {} } diff --git a/moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java b/moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java index 5c65ceb90..cc69e40eb 100644 --- a/moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java +++ b/moshi/src/main/java16/com/squareup/moshi/RecordJsonAdapter.java @@ -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); } }