From 0bcd1b341f2f4f7d93738272f5a6a2c3d2dfb0f7 Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Wed, 4 May 2011 23:26:22 +0000 Subject: [PATCH] Revised Gson to refuse to deserialize floating point numbers into integer types. This is probably a break from the past: previous versions of Gson allowed truncating a floating point into a long or int. However, it wasn't consistent in this behavior. It disallowed converting a BigDecimal value into BigInteger, int or long. Refusing to deserialize such values is aligned with fail-fast approach of uncovering bugs. --- .../java/com/google/gson/JsonPrimitive.java | 3 +- .../com/google/gson/LazilyParsedNumber.java | 18 ++++- .../google/gson/functional/PrimitiveTest.java | 67 +++++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) diff --git a/gson/src/main/java/com/google/gson/JsonPrimitive.java b/gson/src/main/java/com/google/gson/JsonPrimitive.java index 3dad7bae..2caae92d 100644 --- a/gson/src/main/java/com/google/gson/JsonPrimitive.java +++ b/gson/src/main/java/com/google/gson/JsonPrimitive.java @@ -213,7 +213,8 @@ public final class JsonPrimitive extends JsonElement { */ @Override public BigInteger getAsBigInteger() { - return value instanceof BigInteger ? (BigInteger) value : new BigInteger(value.toString()); + return value instanceof BigInteger ? + (BigInteger) value : new BigInteger(value.toString()); } /** diff --git a/gson/src/main/java/com/google/gson/LazilyParsedNumber.java b/gson/src/main/java/com/google/gson/LazilyParsedNumber.java index 0f5a7ee2..6201616d 100644 --- a/gson/src/main/java/com/google/gson/LazilyParsedNumber.java +++ b/gson/src/main/java/com/google/gson/LazilyParsedNumber.java @@ -15,6 +15,8 @@ */ package com.google.gson; +import java.math.BigInteger; + /** * This class holds a number value that is lazily converted to a specific number type * @@ -30,12 +32,24 @@ final class LazilyParsedNumber extends Number { @Override public int intValue() { - return Integer.parseInt(value); + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + try { + return (int) Long.parseLong(value); + } catch (NumberFormatException nfe) { + return new BigInteger(value).intValue(); + } + } } @Override public long longValue() { - return Long.parseLong(value); + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + return new BigInteger(value).longValue(); + } } @Override 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 5ed3f532..847a6b14 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -38,6 +38,9 @@ import java.math.BigInteger; * @author Joel Leitch */ public class PrimitiveTest extends TestCase { + private static final BigInteger MAX_INT_VALUE = new BigInteger("2147483647"); + private static final BigInteger MAX_LONG_VALUE = new BigInteger("9223372036854775807"); + private Gson gson; @Override @@ -757,4 +760,68 @@ public class PrimitiveTest extends TestCase { fail(); } catch (JsonSyntaxException expected) {} } + + public void testDeserializingDecimalPointValuesAsIntegerFails() { + try { + gson.fromJson("1.0", Integer.class); + fail(); + } catch (JsonParseException expected) { + } + } + + public void testDeserializingBigDecimalAsIntegerFails() { + try { + gson.fromJson("-122.08e-213", Integer.class); + fail(); + } catch (JsonParseException expected) { + } + } + + public void testDeserializingBigIntegerAsInteger() { + String bigIntegerValue = "12121211243123245845384534687435634558945453489543985435"; + int actual = gson.fromJson(bigIntegerValue, Integer.class); + int expected = new BigInteger(bigIntegerValue).and(MAX_INT_VALUE).intValue(); + assertEquals(expected, actual); + } + + public void testDeserializingBigIntegerAsLong() { + String bigIntegerValue = "12121211243123245845384534687435634558945453489543985435"; + long actual = gson.fromJson(bigIntegerValue, long.class); + long expected = new BigInteger(bigIntegerValue).and(MAX_LONG_VALUE).longValue(); + assertEquals(expected, actual); + } + + public void testDeserializingBigDecimalAsLongFails() { + try { + gson.fromJson("-122.08e-2132", long.class); + fail(); + } catch (JsonParseException expected) { + } + } + + public void testDeserializingBigDecimalAsFloat() { + String json = "-122.08e-2132332"; + float actual = gson.fromJson(json, float.class); + assertEquals(-0.0f, actual); + } + + public void testDeserializingBigDecimalAsDouble() { + String json = "-122.08e-2132332"; + double actual = gson.fromJson(json, double.class); + assertEquals(-0.0d, actual); + } + + public void testDeserializingBigDecimalAsBigIntegerFails() { + try { + gson.fromJson("-122.08e-213", BigInteger.class); + fail(); + } catch (JsonParseException expected) { + } + } + + public void testDeserializingBigIntegerAsBigDecimal() { + BigDecimal actual = + gson.fromJson("12121211243123245845384534687435634558945453489543985435", BigDecimal.class); + assertEquals("12121211243123245845384534687435634558945453489543985435", actual.toPlainString()); + } }