From 1e4f375e75a9eeddf0ea83b4e030deb2d7eaf92f Mon Sep 17 00:00:00 2001 From: Scott Blum Date: Wed, 13 Aug 2014 19:35:14 -0400 Subject: [PATCH] Finish primitive adapters, add tests. --- .../squareup/moshi/StandardJsonAdapters.java | 124 +++-- .../java/com/squareup/moshi/MoshiTest.java | 451 +++++++++++++++++- 2 files changed, 518 insertions(+), 57 deletions(-) diff --git a/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java b/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java index 64dad04..d9c10a7 100644 --- a/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java +++ b/moshi/src/main/java/com/squareup/moshi/StandardJsonAdapters.java @@ -21,48 +21,100 @@ import java.lang.reflect.Type; final class StandardJsonAdapters { public static final JsonAdapter.Factory FACTORY = new JsonAdapter.Factory() { - @Override public JsonAdapter create( - Type type, AnnotatedElement annotations, Moshi moshi) { - // TODO: support all 8 primitive types. + @Override public JsonAdapter create(Type type, AnnotatedElement annotations, Moshi moshi) { if (type == boolean.class) return BOOLEAN_JSON_ADAPTER; if (type == byte.class) return BYTE_JSON_ADAPTER; - if (type == char.class) return null; + if (type == char.class) return CHARACTER_JSON_ADAPTER; if (type == double.class) return DOUBLE_JSON_ADAPTER; - if (type == float.class) return null; + if (type == float.class) return FLOAT_JSON_ADAPTER; if (type == int.class) return INTEGER_JSON_ADAPTER; if (type == long.class) return LONG_JSON_ADAPTER; - if (type == short.class) return null; + if (type == short.class) return SHORT_JSON_ADAPTER; if (type == Boolean.class) return BOOLEAN_JSON_ADAPTER.nullSafe(); - if (type == byte.class) return BYTE_JSON_ADAPTER.nullSafe(); + if (type == Byte.class) return BYTE_JSON_ADAPTER.nullSafe(); + if (type == Character.class) return CHARACTER_JSON_ADAPTER.nullSafe(); if (type == Double.class) return DOUBLE_JSON_ADAPTER.nullSafe(); + if (type == Float.class) return FLOAT_JSON_ADAPTER.nullSafe(); if (type == Integer.class) return INTEGER_JSON_ADAPTER.nullSafe(); if (type == Long.class) return LONG_JSON_ADAPTER.nullSafe(); + if (type == Short.class) return SHORT_JSON_ADAPTER.nullSafe(); if (type == String.class) return STRING_JSON_ADAPTER.nullSafe(); return null; } }; + private static final String ERROR_FORMAT = "Expected %s but was %s at path %s"; + + private static int rangeCheckNextInt(JsonReader reader, String typeMessage, int min, int max) + throws IOException { + int value = reader.nextInt(); + if (value < min || value > max) { + throw new NumberFormatException( + String.format(ERROR_FORMAT, typeMessage, value, reader.getPath())); + } + return value; + } + static final JsonAdapter BOOLEAN_JSON_ADAPTER = new JsonAdapter() { @Override public Boolean fromJson(JsonReader reader) throws IOException { return reader.nextBoolean(); } + @Override public void toJson(JsonWriter writer, Boolean value) throws IOException { writer.value(value); } }; - static final JsonAdapter BYTE_JSON_ADAPTER = new - IntegerAdapter("a byte", Byte.MIN_VALUE, Byte.MAX_VALUE) { - @Override protected Byte convert(int value) { - return Byte.valueOf((byte) value); - } - }; + static final JsonAdapter BYTE_JSON_ADAPTER = new JsonAdapter() { + @Override public Byte fromJson(JsonReader reader) throws IOException { + return (byte) rangeCheckNextInt(reader, "a byte", Byte.MIN_VALUE, 0xFF); + } + + @Override public void toJson(JsonWriter writer, Byte value) throws IOException { + writer.value(value.intValue() & 0xFF); + } + }; + + static final JsonAdapter CHARACTER_JSON_ADAPTER = new JsonAdapter() { + @Override public Character fromJson(JsonReader reader) throws IOException { + String value = reader.nextString(); + if (value.length() > 1) { + throw new IllegalStateException( + String.format(ERROR_FORMAT, "a char", '"' + value + '"', reader.getPath())); + } + return value.charAt(0); + } + + @Override public void toJson(JsonWriter writer, Character value) throws IOException { + writer.value(value.toString()); + } + }; static final JsonAdapter DOUBLE_JSON_ADAPTER = new JsonAdapter() { @Override public Double fromJson(JsonReader reader) throws IOException { return reader.nextDouble(); } + @Override public void toJson(JsonWriter writer, Double value) throws IOException { + writer.value(value.doubleValue()); + } + }; + + static final JsonAdapter FLOAT_JSON_ADAPTER = new JsonAdapter() { + @Override public Float fromJson(JsonReader reader) throws IOException { + float value = (float) reader.nextDouble(); + if (!reader.isLenient() && Float.isInfinite(value)) { + throw new IOException("JSON forbids NaN and infinities: " + value + + " at path " + reader.getPath()); + } + return value; + } + + @Override public void toJson(JsonWriter writer, Float value) throws IOException { + // Use the Number overload. + if (value == null) { + throw new NullPointerException(); + } writer.value(value); } }; @@ -71,6 +123,7 @@ final class StandardJsonAdapters { @Override public Integer fromJson(JsonReader reader) throws IOException { return reader.nextInt(); } + @Override public void toJson(JsonWriter writer, Integer value) throws IOException { writer.value(value.intValue()); } @@ -80,50 +133,29 @@ final class StandardJsonAdapters { @Override public Long fromJson(JsonReader reader) throws IOException { return reader.nextLong(); } + @Override public void toJson(JsonWriter writer, Long value) throws IOException { writer.value(value.longValue()); } }; + static final JsonAdapter SHORT_JSON_ADAPTER = new JsonAdapter() { + @Override public Short fromJson(JsonReader reader) throws IOException { + return (short) rangeCheckNextInt(reader, "a short", Short.MIN_VALUE, Short.MAX_VALUE); + } + + @Override public void toJson(JsonWriter writer, Short value) throws IOException { + writer.value(value.intValue()); + } + }; + static final JsonAdapter STRING_JSON_ADAPTER = new JsonAdapter() { @Override public String fromJson(JsonReader reader) throws IOException { return reader.nextString(); } + @Override public void toJson(JsonWriter writer, String value) throws IOException { writer.value(value); } }; - - private abstract static class IntegerAdapter extends JsonAdapter { - private final String typeMessage; - private final int min; - private final int max; - - IntegerAdapter(String typeMessage, int min, int max) { - this.typeMessage = typeMessage; - this.min = min; - this.max = max; - } - - @Override public T fromJson(JsonReader reader) throws IOException { - int value = reader.nextInt(); - if (value < min || value > max) { - throw new NumberFormatException("Expected " - + typeMessage - + " but was " - + value - + " at path " - + reader.getPath()); - } - - return convert(value); - } - - protected abstract T convert(int value); - - @Override public void toJson(JsonWriter writer, T value) throws IOException { - writer.value(value.intValue()); - } - } - } diff --git a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java index 3dd5ab8..c8e2c39 100644 --- a/moshi/src/test/java/com/squareup/moshi/MoshiTest.java +++ b/moshi/src/test/java/com/squareup/moshi/MoshiTest.java @@ -34,20 +34,76 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; public final class MoshiTest { - /** No nulls for byte.class. */ + + @Test public void booleanAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(boolean.class).lenient(); + assertThat(adapter.fromJson("true")).isTrue(); + assertThat(adapter.fromJson("TRUE")).isTrue(); + assertThat(adapter.toJson(true)).isEqualTo("true"); + assertThat(adapter.fromJson("false")).isFalse(); + assertThat(adapter.fromJson("FALSE")).isFalse(); + assertThat(adapter.toJson(false)).isEqualTo("false"); + + // Nulls not allowed for boolean.class + try { + adapter.fromJson("null"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a boolean but was NULL at path $"); + } + + try { + adapter.toJson(null); + fail(); + } catch (NullPointerException expected) { + } + } + + @Test public void BooleanAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(Boolean.class).lenient(); + assertThat(adapter.fromJson("true")).isTrue(); + assertThat(adapter.toJson(true)).isEqualTo("true"); + assertThat(adapter.fromJson("false")).isFalse(); + assertThat(adapter.toJson(false)).isEqualTo("false"); + // Allow nulls for Boolean.class + assertThat(adapter.fromJson("null")).isEqualTo(null); + assertThat(adapter.toJson(null)).isEqualTo("null"); + } + @Test public void byteAdapter() throws Exception { Moshi moshi = new Moshi.Builder().build(); JsonAdapter adapter = moshi.adapter(byte.class).lenient(); assertThat(adapter.fromJson("1")).isEqualTo((byte) 1); - assertThat(adapter.toJson((byte) 2)).isEqualTo("2"); + assertThat(adapter.toJson((byte) -2)).isEqualTo("254"); + + // Canonical byte representation is unsigned, but parse the whole range -128..255 + assertThat(adapter.fromJson("-128")).isEqualTo((byte) -128); + assertThat(adapter.fromJson("128")).isEqualTo((byte) -128); + assertThat(adapter.toJson((byte) -128)).isEqualTo("128"); + + assertThat(adapter.fromJson("255")).isEqualTo((byte) -1); + assertThat(adapter.toJson((byte) -1)).isEqualTo("255"); + + assertThat(adapter.fromJson("127")).isEqualTo((byte) 127); + assertThat(adapter.toJson((byte) 127)).isEqualTo("127"); try { - adapter.fromJson("200"); + adapter.fromJson("256"); fail(); } catch (NumberFormatException expected) { - assertThat(expected.getMessage()).isEqualTo("Expected a byte but was 200 at path $"); + assertThat(expected.getMessage()).isEqualTo("Expected a byte but was 256 at path $"); } + try { + adapter.fromJson("-129"); + fail(); + } catch (NumberFormatException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a byte but was -129 at path $"); + } + + // Nulls not allowed for byte.class try { adapter.fromJson("null"); fail(); @@ -56,19 +112,288 @@ public final class MoshiTest { } try { - moshi.adapter(int.class).toJson(null); + adapter.toJson(null); fail(); } catch (NullPointerException expected) { } } - /** No nulls for int.class. */ + @Test public void ByteAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(Byte.class).lenient(); + assertThat(adapter.fromJson("1")).isEqualTo((byte) 1); + assertThat(adapter.toJson((byte) -2)).isEqualTo("254"); + // Allow nulls for Byte.class + assertThat(adapter.fromJson("null")).isEqualTo(null); + assertThat(adapter.toJson(null)).isEqualTo("null"); + } + + @Test public void charAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(char.class).lenient(); + assertThat(adapter.fromJson("\"a\"")).isEqualTo('a'); + assertThat(adapter.fromJson("'a'")).isEqualTo('a'); + assertThat(adapter.toJson('b')).isEqualTo("\"b\""); + + // Exhaustively test all valid characters. Use an int to loop so we can check termination. + for (int i = 0; i <= Character.MAX_VALUE; ++i) { + final char c = (char) i; + String s; + switch (c) { + // TODO: make JsonWriter.REPLACEMENT_CHARS visible for testing? + case '\"': + s = "\\\""; + break; + case '\\': + s = "\\\\"; + break; + case '\t': + s = "\\t"; + break; + case '\b': + s = "\\b"; + break; + case '\n': + s = "\\n"; + break; + case '\r': + s = "\\r"; + break; + case '\f': + s = "\\f"; + break; + case '\u2028': + s = "\\u2028"; + break; + case '\u2029': + s = "\\u2029"; + break; + default: + if (c <= 0x1f) { + s = String.format("\\u%04x", (int) c); + } else if (c >= Character.MIN_SURROGATE && c <= Character.MAX_SURROGATE) { + // TODO: not handled properly; do we need to? + continue; + } else { + s = String.valueOf(c); + } + break; + } + s = '"' + s + '"'; + assertThat(adapter.toJson(c)).isEqualTo(s); + assertThat(adapter.fromJson(s)).isEqualTo(c); + } + + try { + // Only a single character is allowed. + adapter.fromJson("'ab'"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a char but was \"ab\" at path $"); + } + + // Nulls not allowed for char.class + try { + adapter.fromJson("null"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a string but was NULL at path $"); + } + + try { + adapter.toJson(null); + fail(); + } catch (NullPointerException expected) { + } + } + + @Test public void CharacterAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(Character.class).lenient(); + assertThat(adapter.fromJson("\"a\"")).isEqualTo('a'); + assertThat(adapter.fromJson("'a'")).isEqualTo('a'); + assertThat(adapter.toJson('b')).isEqualTo("\"b\""); + + try { + // Only a single character is allowed. + adapter.fromJson("'ab'"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a char but was \"ab\" at path $"); + } + + // Allow nulls for Character.class + assertThat(adapter.fromJson("null")).isEqualTo(null); + assertThat(adapter.toJson(null)).isEqualTo("null"); + } + + @Test public void doubleAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(double.class).lenient(); + assertThat(adapter.fromJson("1.0")).isEqualTo(1.0); + assertThat(adapter.fromJson("1")).isEqualTo(1.0); + assertThat(adapter.fromJson("1e0")).isEqualTo(1.0); + assertThat(adapter.toJson(-2.0)).isEqualTo("-2.0"); + + // Test min/max values. + assertThat(adapter.fromJson("-1.7976931348623157E308")).isEqualTo(-Double.MAX_VALUE); + assertThat(adapter.toJson(-Double.MAX_VALUE)).isEqualTo("-1.7976931348623157E308"); + assertThat(adapter.fromJson("1.7976931348623157E308")).isEqualTo(Double.MAX_VALUE); + assertThat(adapter.toJson(Double.MAX_VALUE)).isEqualTo("1.7976931348623157E308"); + + // Lenient reader converts too large values to infinities. + assertThat(adapter.fromJson("1E309")).isEqualTo(Double.POSITIVE_INFINITY); + assertThat(adapter.fromJson("-1E309")).isEqualTo(Double.NEGATIVE_INFINITY); + assertThat(adapter.fromJson("+Infinity")).isEqualTo(Double.POSITIVE_INFINITY); + assertThat(adapter.fromJson("Infinity")).isEqualTo(Double.POSITIVE_INFINITY); + assertThat(adapter.fromJson("-Infinity")).isEqualTo(Double.NEGATIVE_INFINITY); + + // Nulls not allowed for double.class + try { + adapter.fromJson("null"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a double but was NULL at path $"); + } + + try { + adapter.toJson(null); + fail(); + } catch (NullPointerException expected) { + } + + // Non-lenient adapter won't allow values outside of range. + adapter = moshi.adapter(double.class); + JsonReader reader = new JsonReader("[1E309]"); + reader.beginArray(); + try { + adapter.fromJson(reader); + fail(); + } catch (IOException expected) { + // TODO: should this really be NumberFormatException? + assertThat(expected.getMessage()).isEqualTo("JSON forbids NaN and infinities: Infinity at path $[0]"); + } + + reader = new JsonReader("[-1E309]"); + reader.beginArray(); + try { + adapter.fromJson(reader); + fail(); + } catch (IOException expected) { + // TODO: should this really be NumberFormatException? + assertThat(expected.getMessage()).isEqualTo("JSON forbids NaN and infinities: -Infinity at path $[0]"); + } + } + + @Test public void DoubleAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(Double.class).lenient(); + assertThat(adapter.fromJson("1.0")).isEqualTo(1.0); + assertThat(adapter.fromJson("1")).isEqualTo(1.0); + assertThat(adapter.fromJson("1e0")).isEqualTo(1.0); + assertThat(adapter.toJson(-2.0)).isEqualTo("-2.0"); + // Allow nulls for Double.class + assertThat(adapter.fromJson("null")).isEqualTo(null); + assertThat(adapter.toJson(null)).isEqualTo("null"); + } + + @Test public void floatAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(float.class).lenient(); + assertThat(adapter.fromJson("1.0")).isEqualTo(1.0f); + assertThat(adapter.fromJson("1")).isEqualTo(1.0f); + assertThat(adapter.fromJson("1e0")).isEqualTo(1.0f); + assertThat(adapter.toJson(-2.0f)).isEqualTo("-2.0"); + + // Test min/max values. + assertThat(adapter.fromJson("-3.4028235E38")).isEqualTo(-Float.MAX_VALUE); + assertThat(adapter.toJson(-Float.MAX_VALUE)).isEqualTo("-3.4028235E38"); + assertThat(adapter.fromJson("3.4028235E38")).isEqualTo(Float.MAX_VALUE); + assertThat(adapter.toJson(Float.MAX_VALUE)).isEqualTo("3.4028235E38"); + + // Lenient reader converts too large values to infinities. + assertThat(adapter.fromJson("1E39")).isEqualTo(Float.POSITIVE_INFINITY); + assertThat(adapter.fromJson("-1E39")).isEqualTo(Float.NEGATIVE_INFINITY); + assertThat(adapter.fromJson("+Infinity")).isEqualTo(Float.POSITIVE_INFINITY); + assertThat(adapter.fromJson("Infinity")).isEqualTo(Float.POSITIVE_INFINITY); + assertThat(adapter.fromJson("-Infinity")).isEqualTo(Float.NEGATIVE_INFINITY); + + // Nulls not allowed for float.class + try { + adapter.fromJson("null"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a double but was NULL at path $"); + } + + try { + adapter.toJson(null); + fail(); + } catch (NullPointerException expected) { + } + + // Non-lenient adapter won't allow values outside of range. + adapter = moshi.adapter(float.class); + JsonReader reader = new JsonReader("[1E39]"); + reader.beginArray(); + try { + adapter.fromJson(reader); + fail(); + } catch (IOException expected) { + // TODO: should this really be NumberFormatException? + assertThat(expected.getMessage()).isEqualTo("JSON forbids NaN and infinities: Infinity at path $[1]"); + } + + reader = new JsonReader("[-1E39]"); + reader.beginArray(); + try { + adapter.fromJson(reader); + fail(); + } catch (IOException expected) { + // TODO: should this really be NumberFormatException? + assertThat(expected.getMessage()).isEqualTo("JSON forbids NaN and infinities: -Infinity at path $[1]"); + } + } + + @Test public void FloatAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(Float.class).lenient(); + assertThat(adapter.fromJson("1.0")).isEqualTo(1.0f); + assertThat(adapter.fromJson("1")).isEqualTo(1.0f); + assertThat(adapter.fromJson("1e0")).isEqualTo(1.0f); + assertThat(adapter.toJson(-2.0f)).isEqualTo("-2.0"); + // Allow nulls for Float.class + assertThat(adapter.fromJson("null")).isEqualTo(null); + assertThat(adapter.toJson(null)).isEqualTo("null"); + } + @Test public void intAdapter() throws Exception { Moshi moshi = new Moshi.Builder().build(); JsonAdapter adapter = moshi.adapter(int.class).lenient(); assertThat(adapter.fromJson("1")).isEqualTo(1); - assertThat(adapter.toJson(2)).isEqualTo("2"); + assertThat(adapter.toJson(-2)).isEqualTo("-2"); + // Test min/max values + assertThat(adapter.fromJson("-2147483648")).isEqualTo(Integer.MIN_VALUE); + assertThat(adapter.toJson(Integer.MIN_VALUE)).isEqualTo("-2147483648"); + assertThat(adapter.fromJson("2147483647")).isEqualTo(Integer.MAX_VALUE); + assertThat(adapter.toJson(Integer.MAX_VALUE)).isEqualTo("2147483647"); + + try { + adapter.fromJson("2147483648"); + fail(); + } catch (NumberFormatException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected an int but was 2147483648 at path $"); + } + + try { + adapter.fromJson("-2147483649"); + fail(); + } catch (NumberFormatException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected an int but was -2147483649 at path $"); + } + + // Nulls not allowed for int.class try { adapter.fromJson("null"); fail(); @@ -77,18 +402,122 @@ public final class MoshiTest { } try { - moshi.adapter(int.class).toJson(null); + adapter.toJson(null); fail(); } catch (NullPointerException expected) { } } - /** Moshi supports nulls for Integer.class. */ - @Test public void integerAdapter() throws Exception { + @Test public void IntegerAdapter() throws Exception { Moshi moshi = new Moshi.Builder().build(); JsonAdapter adapter = moshi.adapter(Integer.class).lenient(); assertThat(adapter.fromJson("1")).isEqualTo(1); - assertThat(adapter.toJson(2)).isEqualTo("2"); + assertThat(adapter.toJson(-2)).isEqualTo("-2"); + // Allow nulls for Integer.class + assertThat(adapter.fromJson("null")).isEqualTo(null); + assertThat(adapter.toJson(null)).isEqualTo("null"); + } + + @Test public void longAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(long.class).lenient(); + assertThat(adapter.fromJson("1")).isEqualTo(1L); + assertThat(adapter.toJson(-2L)).isEqualTo("-2"); + + // Test min/max values + assertThat(adapter.fromJson("-9223372036854775808")).isEqualTo(Long.MIN_VALUE); + assertThat(adapter.toJson(Long.MIN_VALUE)).isEqualTo("-9223372036854775808"); + assertThat(adapter.fromJson("9223372036854775807")).isEqualTo(Long.MAX_VALUE); + assertThat(adapter.toJson(Long.MAX_VALUE)).isEqualTo("9223372036854775807"); + + // TODO: This is a bug? + assertThat(adapter.fromJson("9223372036854775808")).isEqualTo(Long.MAX_VALUE); // wtf? + //try { + // adapter.fromJson("9223372036854775808"); + // fail(); + //} catch (NumberFormatException expected) { + // assertThat(expected.getMessage()).isEqualTo("Expected a long but was 9223372036854775808 at path $"); + //} + // + //try { + // adapter.fromJson("-9223372036854775809"); + // fail(); + //} catch (NumberFormatException expected) { + // assertThat(expected.getMessage()).isEqualTo("Expected a long but was -9223372036854775809 at path $"); + //} + + // Nulls not allowed for long.class + try { + adapter.fromJson("null"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a long but was NULL at path $"); + } + + try { + adapter.toJson(null); + fail(); + } catch (NullPointerException expected) { + } + } + + @Test public void LongAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(Long.class).lenient(); + assertThat(adapter.fromJson("1")).isEqualTo(1L); + assertThat(adapter.toJson(-2L)).isEqualTo("-2"); + // Allow nulls for Integer.class + assertThat(adapter.fromJson("null")).isEqualTo(null); + assertThat(adapter.toJson(null)).isEqualTo("null"); + } + + @Test public void shortAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(short.class).lenient(); + assertThat(adapter.fromJson("1")).isEqualTo((short) 1); + assertThat(adapter.toJson((short) -2)).isEqualTo("-2"); + + // Test min/max values. + assertThat(adapter.fromJson("-32768")).isEqualTo(Short.MIN_VALUE); + assertThat(adapter.toJson(Short.MIN_VALUE)).isEqualTo("-32768"); + assertThat(adapter.fromJson("32767")).isEqualTo(Short.MAX_VALUE); + assertThat(adapter.toJson(Short.MAX_VALUE)).isEqualTo("32767"); + + try { + adapter.fromJson("32768"); + fail(); + } catch (NumberFormatException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a short but was 32768 at path $"); + } + + try { + adapter.fromJson("-32769"); + fail(); + } catch (NumberFormatException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected a short but was -32769 at path $"); + } + + // Nulls not allowed for short.class + try { + adapter.fromJson("null"); + fail(); + } catch (IllegalStateException expected) { + assertThat(expected.getMessage()).isEqualTo("Expected an int but was NULL at path $"); + } + + try { + adapter.toJson(null); + fail(); + } catch (NullPointerException expected) { + } + } + + @Test public void ShortAdapter() throws Exception { + Moshi moshi = new Moshi.Builder().build(); + JsonAdapter adapter = moshi.adapter(Short.class).lenient(); + assertThat(adapter.fromJson("1")).isEqualTo((short) 1); + assertThat(adapter.toJson((short) -2)).isEqualTo("-2"); + // Allow nulls for Byte.class assertThat(adapter.fromJson("null")).isEqualTo(null); assertThat(adapter.toJson(null)).isEqualTo("null"); }