From 6a9d240f786bfe6602ae5dafd6da23b4fb3043fc Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 23 Oct 2023 22:51:29 +0200 Subject: [PATCH] Add limits when deserializing `BigDecimal` and `BigInteger` (#2510) * Add limits when deserializing `BigDecimal` and `BigInteger` * Use assertThrows * Don't check number limits in JsonReader --- .../java/com/google/gson/JsonPrimitive.java | 5 +- .../java/com/google/gson/ToNumberPolicy.java | 3 +- .../gson/internal/LazilyParsedNumber.java | 10 +- .../google/gson/internal/NumberLimits.java | 37 +++ .../gson/internal/bind/TypeAdapters.java | 5 +- .../gson/functional/NumberLimitsTest.java | 185 +++++++++++++++ .../google/gson/functional/PrimitiveTest.java | 221 ++++-------------- 7 files changed, 284 insertions(+), 182 deletions(-) create mode 100644 gson/src/main/java/com/google/gson/internal/NumberLimits.java create mode 100644 gson/src/test/java/com/google/gson/functional/NumberLimitsTest.java diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index 7095c05a35..190439617b 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -17,6 +17,7 @@ package com.google.gson; import com.google.gson.internal.LazilyParsedNumber; +import com.google.gson.internal.NumberLimits; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Objects; @@ -172,7 +173,7 @@ public double getAsDouble() { */ @Override public BigDecimal getAsBigDecimal() { - return value instanceof BigDecimal ? (BigDecimal) value : new BigDecimal(getAsString()); + return value instanceof BigDecimal ? (BigDecimal) value : NumberLimits.parseBigDecimal(getAsString()); } /** @@ -184,7 +185,7 @@ public BigInteger getAsBigInteger() { ? (BigInteger) value : isIntegral(this) ? BigInteger.valueOf(this.getAsNumber().longValue()) - : new BigInteger(this.getAsString()); + : NumberLimits.parseBigInteger(this.getAsString()); } /** diff --git a/gson/src/main/java/com/google/gson/ToNumberPolicy.java b/gson/src/main/java/com/google/gson/ToNumberPolicy.java index 8689298490..34f4d4f9ea 100644 --- a/gson/src/main/java/com/google/gson/ToNumberPolicy.java +++ b/gson/src/main/java/com/google/gson/ToNumberPolicy.java @@ -17,6 +17,7 @@ package com.google.gson; import com.google.gson.internal.LazilyParsedNumber; +import com.google.gson.internal.NumberLimits; import com.google.gson.stream.JsonReader; import com.google.gson.stream.MalformedJsonException; import java.io.IOException; @@ -89,7 +90,7 @@ public enum ToNumberPolicy implements ToNumberStrategy { @Override public BigDecimal readNumber(JsonReader in) throws IOException { String value = in.nextString(); try { - return new BigDecimal(value); + return NumberLimits.parseBigDecimal(value); } catch (NumberFormatException e) { throw new JsonParseException("Cannot parse " + value + "; at path " + in.getPreviousPath(), e); } diff --git a/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java b/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java index 6385bc9633..3358b485fe 100644 --- a/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java +++ b/gson/src/main/java/com/google/gson/internal/LazilyParsedNumber.java @@ -35,6 +35,10 @@ public LazilyParsedNumber(String value) { this.value = value; } + private BigDecimal asBigDecimal() { + return NumberLimits.parseBigDecimal(value); + } + @Override public int intValue() { try { @@ -43,7 +47,7 @@ public int intValue() { try { return (int) Long.parseLong(value); } catch (NumberFormatException nfe) { - return new BigDecimal(value).intValue(); + return asBigDecimal().intValue(); } } } @@ -53,7 +57,7 @@ public long longValue() { try { return Long.parseLong(value); } catch (NumberFormatException e) { - return new BigDecimal(value).longValue(); + return asBigDecimal().longValue(); } } @@ -78,7 +82,7 @@ public String toString() { * deserialize it. */ private Object writeReplace() throws ObjectStreamException { - return new BigDecimal(value); + return asBigDecimal(); } private void readObject(ObjectInputStream in) throws IOException { diff --git a/gson/src/main/java/com/google/gson/internal/NumberLimits.java b/gson/src/main/java/com/google/gson/internal/NumberLimits.java new file mode 100644 index 0000000000..09c349c6d8 --- /dev/null +++ b/gson/src/main/java/com/google/gson/internal/NumberLimits.java @@ -0,0 +1,37 @@ +package com.google.gson.internal; + +import java.math.BigDecimal; +import java.math.BigInteger; + +/** + * This class enforces limits on numbers parsed from JSON to avoid potential performance + * problems when extremely large numbers are used. + */ +public class NumberLimits { + private NumberLimits() { + } + + private static final int MAX_NUMBER_STRING_LENGTH = 10_000; + + private static void checkNumberStringLength(String s) { + if (s.length() > MAX_NUMBER_STRING_LENGTH) { + throw new NumberFormatException("Number string too large: " + s.substring(0, 30) + "..."); + } + } + + public static BigDecimal parseBigDecimal(String s) throws NumberFormatException { + checkNumberStringLength(s); + BigDecimal decimal = new BigDecimal(s); + + // Cast to long to avoid issues with abs when value is Integer.MIN_VALUE + if (Math.abs((long) decimal.scale()) >= 10_000) { + throw new NumberFormatException("Number has unsupported scale: " + s); + } + return decimal; + } + + public static BigInteger parseBigInteger(String s) throws NumberFormatException { + checkNumberStringLength(s); + return new BigInteger(s); + } +} diff --git a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java index 152f0482af..ec46db1d1d 100644 --- a/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java +++ b/gson/src/main/java/com/google/gson/internal/bind/TypeAdapters.java @@ -28,6 +28,7 @@ import com.google.gson.TypeAdapterFactory; import com.google.gson.annotations.SerializedName; import com.google.gson.internal.LazilyParsedNumber; +import com.google.gson.internal.NumberLimits; import com.google.gson.internal.TroubleshootingGuide; import com.google.gson.reflect.TypeToken; import com.google.gson.stream.JsonReader; @@ -437,7 +438,7 @@ public void write(JsonWriter out, String value) throws IOException { } String s = in.nextString(); try { - return new BigDecimal(s); + return NumberLimits.parseBigDecimal(s); } catch (NumberFormatException e) { throw new JsonSyntaxException("Failed parsing '" + s + "' as BigDecimal; at path " + in.getPreviousPath(), e); } @@ -456,7 +457,7 @@ public void write(JsonWriter out, String value) throws IOException { } String s = in.nextString(); try { - return new BigInteger(s); + return NumberLimits.parseBigInteger(s); } catch (NumberFormatException e) { throw new JsonSyntaxException("Failed parsing '" + s + "' as BigInteger; at path " + in.getPreviousPath(), e); } diff --git a/gson/src/test/java/com/google/gson/functional/NumberLimitsTest.java b/gson/src/test/java/com/google/gson/functional/NumberLimitsTest.java new file mode 100644 index 0000000000..3a4aec011a --- /dev/null +++ b/gson/src/test/java/com/google/gson/functional/NumberLimitsTest.java @@ -0,0 +1,185 @@ +package com.google.gson.functional; + +import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSyntaxException; +import com.google.gson.ToNumberPolicy; +import com.google.gson.ToNumberStrategy; +import com.google.gson.TypeAdapter; +import com.google.gson.internal.LazilyParsedNumber; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.MalformedJsonException; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.StringReader; +import java.math.BigDecimal; +import java.math.BigInteger; +import org.junit.Test; + +public class NumberLimitsTest { + private static final int MAX_LENGTH = 10_000; + + private static JsonReader jsonReader(String json) { + return new JsonReader(new StringReader(json)); + } + + /** + * Tests how {@link JsonReader} behaves for large numbers. + * + *

Currently {@link JsonReader} itself does not enforce any limits. + * The reasons for this are: + *

+ */ + @Test + public void testJsonReader() throws IOException { + JsonReader reader = jsonReader("1".repeat(1000)); + assertThat(reader.peek()).isEqualTo(JsonToken.NUMBER); + assertThat(reader.nextString()).isEqualTo("1".repeat(1000)); + + JsonReader reader2 = jsonReader("1".repeat(MAX_LENGTH + 1)); + // Currently JsonReader does not recognize large JSON numbers as numbers but treats them + // as unquoted string + MalformedJsonException e = assertThrows(MalformedJsonException.class, () -> reader2.peek()); + assertThat(e).hasMessageThat().startsWith("Use JsonReader.setStrictness(Strictness.LENIENT) to accept malformed JSON"); + + reader = jsonReader("1e9999"); + assertThat(reader.peek()).isEqualTo(JsonToken.NUMBER); + assertThat(reader.nextString()).isEqualTo("1e9999"); + + reader = jsonReader("1e+9999"); + assertThat(reader.peek()).isEqualTo(JsonToken.NUMBER); + assertThat(reader.nextString()).isEqualTo("1e+9999"); + + reader = jsonReader("1e10000"); + assertThat(reader.peek()).isEqualTo(JsonToken.NUMBER); + assertThat(reader.nextString()).isEqualTo("1e10000"); + + reader = jsonReader("1e00001"); + assertThat(reader.peek()).isEqualTo(JsonToken.NUMBER); + assertThat(reader.nextString()).isEqualTo("1e00001"); + } + + @Test + public void testJsonPrimitive() { + assertThat(new JsonPrimitive("1".repeat(MAX_LENGTH)).getAsBigDecimal()) + .isEqualTo(new BigDecimal("1".repeat(MAX_LENGTH))); + assertThat(new JsonPrimitive("1e9999").getAsBigDecimal()) + .isEqualTo(new BigDecimal("1e9999")); + assertThat(new JsonPrimitive("1e-9999").getAsBigDecimal()) + .isEqualTo(new BigDecimal("1e-9999")); + + NumberFormatException e = assertThrows(NumberFormatException.class, + () -> new JsonPrimitive("1".repeat(MAX_LENGTH + 1)).getAsBigDecimal()); + assertThat(e).hasMessageThat().isEqualTo("Number string too large: 111111111111111111111111111111..."); + + e = assertThrows(NumberFormatException.class, + () -> new JsonPrimitive("1e10000").getAsBigDecimal()); + assertThat(e).hasMessageThat().isEqualTo("Number has unsupported scale: 1e10000"); + + e = assertThrows(NumberFormatException.class, + () -> new JsonPrimitive("1e-10000").getAsBigDecimal()); + assertThat(e).hasMessageThat().isEqualTo("Number has unsupported scale: 1e-10000"); + + + assertThat(new JsonPrimitive("1".repeat(MAX_LENGTH)).getAsBigInteger()) + .isEqualTo(new BigInteger("1".repeat(MAX_LENGTH))); + + e = assertThrows(NumberFormatException.class, + () -> new JsonPrimitive("1".repeat(MAX_LENGTH + 1)).getAsBigInteger()); + assertThat(e).hasMessageThat().isEqualTo("Number string too large: 111111111111111111111111111111..."); + } + + @Test + public void testToNumberPolicy() throws IOException { + ToNumberStrategy strategy = ToNumberPolicy.BIG_DECIMAL; + + assertThat(strategy.readNumber(jsonReader("\"" + "1".repeat(MAX_LENGTH) + "\""))) + .isEqualTo(new BigDecimal("1".repeat(MAX_LENGTH))); + assertThat(strategy.readNumber(jsonReader("1e9999"))) + .isEqualTo(new BigDecimal("1e9999")); + + + JsonParseException e = assertThrows(JsonParseException.class, + () -> strategy.readNumber(jsonReader("\"" + "1".repeat(MAX_LENGTH + 1) + "\""))); + assertThat(e).hasMessageThat().isEqualTo("Cannot parse " + "1".repeat(MAX_LENGTH + 1) + "; at path $"); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Number string too large: 111111111111111111111111111111..."); + + e = assertThrows(JsonParseException.class, () -> strategy.readNumber(jsonReader("\"1e10000\""))); + assertThat(e).hasMessageThat().isEqualTo("Cannot parse 1e10000; at path $"); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Number has unsupported scale: 1e10000"); + } + + @Test + public void testLazilyParsedNumber() throws IOException { + assertThat(new LazilyParsedNumber("1".repeat(MAX_LENGTH)).intValue()) + .isEqualTo(new BigDecimal("1".repeat(MAX_LENGTH)).intValue()); + assertThat(new LazilyParsedNumber("1e9999").intValue()) + .isEqualTo(new BigDecimal("1e9999").intValue()); + + NumberFormatException e = assertThrows(NumberFormatException.class, + () -> new LazilyParsedNumber("1".repeat(MAX_LENGTH + 1)).intValue()); + assertThat(e).hasMessageThat().isEqualTo("Number string too large: 111111111111111111111111111111..."); + + e = assertThrows(NumberFormatException.class, + () -> new LazilyParsedNumber("1e10000").intValue()); + assertThat(e).hasMessageThat().isEqualTo("Number has unsupported scale: 1e10000"); + + e = assertThrows(NumberFormatException.class, + () -> new LazilyParsedNumber("1e10000").longValue()); + assertThat(e).hasMessageThat().isEqualTo("Number has unsupported scale: 1e10000"); + + ObjectOutputStream objOut = new ObjectOutputStream(OutputStream.nullOutputStream()); + // Number is serialized as BigDecimal; should also enforce limits during this conversion + e = assertThrows(NumberFormatException.class, () -> objOut.writeObject(new LazilyParsedNumber("1e10000"))); + assertThat(e).hasMessageThat().isEqualTo("Number has unsupported scale: 1e10000"); + } + + @Test + public void testBigDecimalAdapter() throws IOException { + TypeAdapter adapter = new Gson().getAdapter(BigDecimal.class); + + assertThat(adapter.fromJson("\"" + "1".repeat(MAX_LENGTH) + "\"")) + .isEqualTo(new BigDecimal("1".repeat(MAX_LENGTH))); + assertThat(adapter.fromJson("\"1e9999\"")) + .isEqualTo(new BigDecimal("1e9999")); + + JsonSyntaxException e = assertThrows(JsonSyntaxException.class, + () -> adapter.fromJson("\"" + "1".repeat(MAX_LENGTH + 1) + "\"")); + assertThat(e).hasMessageThat().isEqualTo("Failed parsing '" + "1".repeat(MAX_LENGTH + 1) + "' as BigDecimal; at path $"); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Number string too large: 111111111111111111111111111111..."); + + e = assertThrows(JsonSyntaxException.class, + () -> adapter.fromJson("\"1e10000\"")); + assertThat(e).hasMessageThat().isEqualTo("Failed parsing '1e10000' as BigDecimal; at path $"); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Number has unsupported scale: 1e10000"); + } + + @Test + public void testBigIntegerAdapter() throws IOException { + TypeAdapter adapter = new Gson().getAdapter(BigInteger.class); + + assertThat(adapter.fromJson("\"" + "1".repeat(MAX_LENGTH) + "\"")) + .isEqualTo(new BigInteger("1".repeat(MAX_LENGTH))); + + JsonSyntaxException e = assertThrows(JsonSyntaxException.class, + () -> adapter.fromJson("\"" + "1".repeat(MAX_LENGTH + 1) + "\"")); + assertThat(e).hasMessageThat().isEqualTo("Failed parsing '" + "1".repeat(MAX_LENGTH + 1) + "' as BigInteger; at path $"); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Number string too large: 111111111111111111111111111111..."); + } +} diff --git a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java index 244d288371..0bfb4754cc 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -17,6 +17,7 @@ package com.google.gson.functional; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.fail; import com.google.gson.Gson; @@ -88,26 +89,14 @@ public void testByteDeserialization() { @Test public void testByteDeserializationLossy() { - try { - gson.fromJson("-129", byte.class); - fail(); - } catch (JsonSyntaxException e) { - assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from -129 to byte; at path $"); - } + JsonSyntaxException e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson("-129", byte.class)); + assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from -129 to byte; at path $"); - try { - gson.fromJson("256", byte.class); - fail(); - } catch (JsonSyntaxException e) { - assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from 256 to byte; at path $"); - } + e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson("256", byte.class)); + assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from 256 to byte; at path $"); - try { - gson.fromJson("2147483648", byte.class); - fail(); - } catch (JsonSyntaxException e) { - assertThat(e).hasMessageThat().isEqualTo("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $"); - } + e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson("2147483648", byte.class)); + assertThat(e).hasMessageThat().isEqualTo("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $"); } @Test @@ -136,26 +125,14 @@ public void testShortDeserialization() { @Test public void testShortDeserializationLossy() { - try { - gson.fromJson("-32769", short.class); - fail(); - } catch (JsonSyntaxException e) { - assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from -32769 to short; at path $"); - } + JsonSyntaxException e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson("-32769", short.class)); + assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from -32769 to short; at path $"); - try { - gson.fromJson("65536", short.class); - fail(); - } catch (JsonSyntaxException e) { - assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from 65536 to short; at path $"); - } + e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson("65536", short.class)); + assertThat(e).hasMessageThat().isEqualTo("Lossy conversion from 65536 to short; at path $"); - try { - gson.fromJson("2147483648", short.class); - fail(); - } catch (JsonSyntaxException e) { - assertThat(e).hasMessageThat().isEqualTo("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $"); - } + e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson("2147483648", short.class)); + assertThat(e).hasMessageThat().isEqualTo("java.lang.NumberFormatException: Expected an int but was 2147483648 at line 1 column 11 path $"); } @Test @@ -768,10 +745,7 @@ public void testUnquotedStringDeserializationFails() { assertThat(gson.fromJson("UnquotedSingleWord", String.class)).isEqualTo("UnquotedSingleWord"); String value = "String Blah Blah Blah...1, 2, 3"; - try { - gson.fromJson(value, String.class); - fail(); - } catch (JsonSyntaxException expected) { } + assertThrows(JsonSyntaxException.class, () -> gson.fromJson(value, String.class)); } @Test @@ -805,162 +779,102 @@ public void testPrimitiveClassLiteral() { @Test public void testDeserializeJsonObjectAsLongPrimitive() { - try { - gson.fromJson("{'abc':1}", long.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'abc':1}", long.class)); } @Test public void testDeserializeJsonArrayAsLongWrapper() { - try { - gson.fromJson("[1,2,3]", Long.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1,2,3]", Long.class)); } @Test public void testDeserializeJsonArrayAsInt() { - try { - gson.fromJson("[1, 2, 3, 4]", int.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1, 2, 3, 4]", int.class)); } @Test public void testDeserializeJsonObjectAsInteger() { - try { - gson.fromJson("{}", Integer.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{}", Integer.class)); } @Test public void testDeserializeJsonObjectAsShortPrimitive() { - try { - gson.fromJson("{'abc':1}", short.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'abc':1}", short.class)); } @Test public void testDeserializeJsonArrayAsShortWrapper() { - try { - gson.fromJson("['a','b']", Short.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("['a','b']", Short.class)); } @Test public void testDeserializeJsonArrayAsDoublePrimitive() { - try { - gson.fromJson("[1,2]", double.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1,2]", double.class)); } @Test public void testDeserializeJsonObjectAsDoubleWrapper() { - try { - gson.fromJson("{'abc':1}", Double.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'abc':1}", Double.class)); } @Test public void testDeserializeJsonObjectAsFloatPrimitive() { - try { - gson.fromJson("{'abc':1}", float.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'abc':1}", float.class)); } @Test public void testDeserializeJsonArrayAsFloatWrapper() { - try { - gson.fromJson("[1,2,3]", Float.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1,2,3]", Float.class)); } @Test public void testDeserializeJsonObjectAsBytePrimitive() { - try { - gson.fromJson("{'abc':1}", byte.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'abc':1}", byte.class)); } @Test public void testDeserializeJsonArrayAsByteWrapper() { - try { - gson.fromJson("[1,2,3,4]", Byte.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1,2,3,4]", Byte.class)); } @Test public void testDeserializeJsonObjectAsBooleanPrimitive() { - try { - gson.fromJson("{'abc':1}", boolean.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'abc':1}", boolean.class)); } @Test public void testDeserializeJsonArrayAsBooleanWrapper() { - try { - gson.fromJson("[1,2,3,4]", Boolean.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1,2,3,4]", Boolean.class)); } @Test public void testDeserializeJsonArrayAsBigDecimal() { - try { - gson.fromJson("[1,2,3,4]", BigDecimal.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1,2,3,4]", BigDecimal.class)); } @Test public void testDeserializeJsonObjectAsBigDecimal() { - try { - gson.fromJson("{'a':1}", BigDecimal.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'a':1}", BigDecimal.class)); } @Test public void testDeserializeJsonArrayAsBigInteger() { - try { - gson.fromJson("[1,2,3,4]", BigInteger.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1,2,3,4]", BigInteger.class)); } @Test public void testDeserializeJsonObjectAsBigInteger() { - try { - gson.fromJson("{'c':2}", BigInteger.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'c':2}", BigInteger.class)); } @Test public void testDeserializeJsonArrayAsNumber() { - try { - gson.fromJson("[1,2,3,4]", Number.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("[1,2,3,4]", Number.class)); } @Test public void testDeserializeJsonObjectAsNumber() { - try { - gson.fromJson("{'c':2}", Number.class); - fail(); - } catch (JsonSyntaxException expected) {} + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("{'c':2}", Number.class)); } @Test @@ -970,53 +884,30 @@ public void testDeserializingDecimalPointValueZeroSucceeds() { @Test public void testDeserializingNonZeroDecimalPointValuesAsIntegerFails() { - try { - gson.fromJson("1.02", Byte.class); - fail(); - } catch (JsonSyntaxException expected) { - } - try { - gson.fromJson("1.02", Short.class); - fail(); - } catch (JsonSyntaxException expected) { - } - try { - gson.fromJson("1.02", Integer.class); - fail(); - } catch (JsonSyntaxException expected) { - } - try { - gson.fromJson("1.02", Long.class); - fail(); - } catch (JsonSyntaxException expected) { - } + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("1.02", Byte.class)); + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("1.02", Short.class)); + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("1.02", Integer.class)); + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("1.02", Long.class)); } @Test public void testDeserializingBigDecimalAsIntegerFails() { - try { - gson.fromJson("-122.08e-213", Integer.class); - fail(); - } catch (JsonSyntaxException expected) { - } + JsonSyntaxException e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson("-122.08e-213", Integer.class)); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Expected an int but was -122.08e-213 at line 1 column 13 path $"); } @Test public void testDeserializingBigIntegerAsInteger() { - try { - gson.fromJson("12121211243123245845384534687435634558945453489543985435", Integer.class); - fail(); - } catch (JsonSyntaxException expected) { - } + String number = "12121211243123245845384534687435634558945453489543985435"; + JsonSyntaxException e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson(number, Integer.class)); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Expected an int but was " + number + " at line 1 column 57 path $"); } @Test public void testDeserializingBigIntegerAsLong() { - try { - gson.fromJson("12121211243123245845384534687435634558945453489543985435", Long.class); - fail(); - } catch (JsonSyntaxException expected) { - } + String number = "12121211243123245845384534687435634558945453489543985435"; + JsonSyntaxException e = assertThrows(JsonSyntaxException.class, () -> gson.fromJson(number, Long.class)); + assertThat(e).hasCauseThat().hasMessageThat().isEqualTo("Expected a long but was " + number + " at line 1 column 57 path $"); } @Test @@ -1031,27 +922,9 @@ public void testValueVeryCloseToZeroIsZero() { assertThat(gson.fromJson("122.08e-2132", double.class)).isEqualTo(0.0); } - @Test - public void testDeserializingBigDecimalAsFloat() { - String json = "-122.08e-2132332"; - float actual = gson.fromJson(json, float.class); - assertThat(actual).isEqualTo(-0.0f); - } - - @Test - public void testDeserializingBigDecimalAsDouble() { - String json = "-122.08e-2132332"; - double actual = gson.fromJson(json, double.class); - assertThat(actual).isEqualTo(-0.0d); - } - @Test public void testDeserializingBigDecimalAsBigIntegerFails() { - try { - gson.fromJson("-122.08e-213", BigInteger.class); - fail(); - } catch (JsonSyntaxException expected) { - } + assertThrows(JsonSyntaxException.class, () -> gson.fromJson("-122.08e-213", BigInteger.class)); } @Test