From 0b6937f6963774b7a86195948e9542698171b9bb Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 25 Jul 2022 23:12:54 +0200 Subject: [PATCH 1/3] Perform numeric conversion for primitive numeric type adapters This should probably not be visible to the user unless they use the non-typesafe `Gson.toJson(Object, Type)` where unrelated number types can be used, or when malformed generic containers are used. For example a `List` containing a Float. This change also has the advantage of avoiding `JsonWriter.value(Number)` for primitive type adapters. That method has some overhead because it needs to make sure that the value is a valid JSON number. However, for primitive numbers this check is redundant. --- gson/src/main/java/com/google/gson/Gson.java | 4 +- .../gson/internal/bind/TypeAdapters.java | 36 +++++++++-- .../google/gson/functional/PrimitiveTest.java | 60 +++++++++++++++++++ 3 files changed, 92 insertions(+), 8 deletions(-) diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 666e5f8bd3..7b3413d63a 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -398,7 +398,7 @@ private TypeAdapter doubleAdapter(boolean serializeSpecialFloatingPointV } double doubleValue = value.doubleValue(); checkValidFloatingPoint(doubleValue); - out.value(value); + out.value(doubleValue); } }; } @@ -422,7 +422,7 @@ private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointVa } float floatValue = value.floatValue(); checkValidFloatingPoint(floatValue); - out.value(value); + out.value(floatValue); } }; } 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 9ba136379d..cbfd13f23d 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 @@ -194,7 +194,11 @@ public Number read(JsonReader in) throws IOException { } @Override public void write(JsonWriter out, Number value) throws IOException { - out.value(value); + if (value == null) { + out.nullValue(); + } else { + out.value(value.byteValue()); + } } }; @@ -223,7 +227,11 @@ public Number read(JsonReader in) throws IOException { } @Override public void write(JsonWriter out, Number value) throws IOException { - out.value(value); + if (value == null) { + out.nullValue(); + } else { + out.value(value.shortValue()); + } } }; @@ -245,7 +253,11 @@ public Number read(JsonReader in) throws IOException { } @Override public void write(JsonWriter out, Number value) throws IOException { - out.value(value); + if (value == null) { + out.nullValue(); + } else { + out.value(value.intValue()); + } } }; public static final TypeAdapterFactory INTEGER_FACTORY @@ -323,7 +335,11 @@ public Number read(JsonReader in) throws IOException { } @Override public void write(JsonWriter out, Number value) throws IOException { - out.value(value); + if (value == null) { + out.nullValue(); + } else { + out.value(value.longValue()); + } } }; @@ -338,7 +354,11 @@ public Number read(JsonReader in) throws IOException { } @Override public void write(JsonWriter out, Number value) throws IOException { - out.value(value); + if (value == null) { + out.nullValue(); + } else { + out.value(value.floatValue()); + } } }; @@ -353,7 +373,11 @@ public Number read(JsonReader in) throws IOException { } @Override public void write(JsonWriter out, Number value) throws IOException { - out.value(value); + if (value == null) { + out.nullValue(); + } else { + out.value(value.doubleValue()); + } } }; 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 6d74cc29b3..c4c25f0072 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -64,6 +64,11 @@ public void testPrimitiveIntegerAutoboxedDeserialization() { public void testByteSerialization() { assertEquals("1", gson.toJson(1, byte.class)); assertEquals("1", gson.toJson(1, Byte.class)); + assertEquals(Byte.toString(Byte.MIN_VALUE), gson.toJson(Byte.MIN_VALUE, Byte.class)); + assertEquals(Byte.toString(Byte.MAX_VALUE), gson.toJson(Byte.MAX_VALUE, Byte.class)); + // Should perform narrowing conversion + assertEquals("-128", gson.toJson(128, Byte.class)); + assertEquals("1", gson.toJson(1.5, Byte.class)); } public void testByteDeserialization() { @@ -102,6 +107,13 @@ public void testByteDeserializationLossy() { public void testShortSerialization() { assertEquals("1", gson.toJson(1, short.class)); assertEquals("1", gson.toJson(1, Short.class)); + assertEquals(Short.toString(Short.MIN_VALUE), gson.toJson(Short.MIN_VALUE, Short.class)); + assertEquals(Short.toString(Short.MAX_VALUE), gson.toJson(Short.MAX_VALUE, Short.class)); + // Should perform widening conversion + assertEquals("1", gson.toJson((byte) 1, Short.class)); + // Should perform narrowing conversion + assertEquals("-32768", gson.toJson(32768, Short.class)); + assertEquals("1", gson.toJson(1.5, Short.class)); } public void testShortDeserialization() { @@ -137,6 +149,54 @@ public void testShortDeserializationLossy() { } } + public void testIntSerialization() { + assertEquals("1", gson.toJson(1, int.class)); + assertEquals("1", gson.toJson(1, Integer.class)); + assertEquals(Integer.toString(Integer.MIN_VALUE), gson.toJson(Integer.MIN_VALUE, Integer.class)); + assertEquals(Integer.toString(Integer.MAX_VALUE), gson.toJson(Integer.MAX_VALUE, Integer.class)); + // Should perform widening conversion + assertEquals("1", gson.toJson((byte) 1, Integer.class)); + // Should perform narrowing conversion + assertEquals("-2147483648", gson.toJson(2147483648L, Integer.class)); + assertEquals("1", gson.toJson(1.5, Integer.class)); + } + + public void testLongSerialization() { + assertEquals("1", gson.toJson(1L, long.class)); + assertEquals("1", gson.toJson(1L, Long.class)); + assertEquals(Long.toString(Long.MIN_VALUE), gson.toJson(Long.MIN_VALUE, Long.class)); + assertEquals(Long.toString(Long.MAX_VALUE), gson.toJson(Long.MAX_VALUE, Long.class)); + // Should perform widening conversion + assertEquals("1", gson.toJson((byte) 1, Long.class)); + // Should perform narrowing conversion + assertEquals("1", gson.toJson(1.5, Long.class)); + } + + public void testFloatSerialization() { + assertEquals("1.5", gson.toJson(1.5f, float.class)); + assertEquals("1.5", gson.toJson(1.5f, Float.class)); + assertEquals(Float.toString(Float.MIN_VALUE), gson.toJson(Float.MIN_VALUE, Float.class)); + assertEquals(Float.toString(Float.MAX_VALUE), gson.toJson(Float.MAX_VALUE, Float.class)); + // Should perform widening conversion + assertEquals("1.0", gson.toJson((byte) 1, Float.class)); + // (This widening conversion is actually lossy) + assertEquals(Float.toString(Long.MAX_VALUE - 10L), gson.toJson(Long.MAX_VALUE - 10L, Float.class)); + // Should perform narrowing conversion + gson = new GsonBuilder().serializeSpecialFloatingPointValues().create(); + assertEquals("Infinity", gson.toJson(Double.MAX_VALUE, Float.class)); + } + + public void testDoubleSerialization() { + assertEquals("1.5", gson.toJson(1.5, double.class)); + assertEquals("1.5", gson.toJson(1.5, Double.class)); + assertEquals(Double.toString(Double.MIN_VALUE), gson.toJson(Double.MIN_VALUE, Double.class)); + assertEquals(Double.toString(Double.MAX_VALUE), gson.toJson(Double.MAX_VALUE, Double.class)); + // Should perform widening conversion + assertEquals("1.0", gson.toJson((byte) 1, Double.class)); + // (This widening conversion is actually lossy) + assertEquals(Double.toString(Long.MAX_VALUE - 10L), gson.toJson(Long.MAX_VALUE - 10L, Double.class)); + } + public void testPrimitiveIntegerAutoboxedInASingleElementArraySerialization() { int target[] = {-9332}; assertEquals("[-9332]", gson.toJson(target)); From 9768d6bf64730023a10a1ec4249213630dd07493 Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 3 Oct 2022 17:16:16 +0200 Subject: [PATCH 2/3] Don't call `JsonWriter.value(float)` for backward compatibility --- gson/src/main/java/com/google/gson/Gson.java | 5 ++++- .../java/com/google/gson/internal/bind/TypeAdapters.java | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 7b3413d63a..215fb5d241 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -422,7 +422,10 @@ private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointVa } float floatValue = value.floatValue(); checkValidFloatingPoint(floatValue); - out.value(floatValue); + // For backward compatibility don't call `JsonWriter.value(float)` because that methods has + // been newly added and not all custom JsonWriter implementations might override it yet + Number floatNumber = value instanceof Float ? value : floatValue; + out.value(floatNumber); } }; } 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 cbfd13f23d..a48964c308 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 @@ -357,7 +357,10 @@ public void write(JsonWriter out, Number value) throws IOException { if (value == null) { out.nullValue(); } else { - out.value(value.floatValue()); + // For backward compatibility don't call `JsonWriter.value(float)` because that methods has + // been newly added and not all custom JsonWriter implementations might override it yet + Number floatNumber = value instanceof Float ? value : value.floatValue(); + out.value(floatNumber); } } }; From e7a5a0810d8838902ec3fd33c0843526999a341e Mon Sep 17 00:00:00 2001 From: Marcono1234 Date: Mon, 3 Oct 2022 20:33:16 +0200 Subject: [PATCH 3/3] Fix typo in comments --- gson/src/main/java/com/google/gson/Gson.java | 2 +- .../main/java/com/google/gson/internal/bind/TypeAdapters.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gson/src/main/java/com/google/gson/Gson.java b/gson/src/main/java/com/google/gson/Gson.java index 215fb5d241..7edead76ec 100644 --- a/gson/src/main/java/com/google/gson/Gson.java +++ b/gson/src/main/java/com/google/gson/Gson.java @@ -422,7 +422,7 @@ private TypeAdapter floatAdapter(boolean serializeSpecialFloatingPointVa } float floatValue = value.floatValue(); checkValidFloatingPoint(floatValue); - // For backward compatibility don't call `JsonWriter.value(float)` because that methods has + // For backward compatibility don't call `JsonWriter.value(float)` because that method has // been newly added and not all custom JsonWriter implementations might override it yet Number floatNumber = value instanceof Float ? value : floatValue; out.value(floatNumber); 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 a48964c308..8eb74a9f52 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 @@ -357,7 +357,7 @@ public void write(JsonWriter out, Number value) throws IOException { if (value == null) { out.nullValue(); } else { - // For backward compatibility don't call `JsonWriter.value(float)` because that methods has + // For backward compatibility don't call `JsonWriter.value(float)` because that method has // been newly added and not all custom JsonWriter implementations might override it yet Number floatNumber = value instanceof Float ? value : value.floatValue(); out.value(floatNumber);