From 8d4d95bef633dfbdefa472daabe0f4863cd4b880 Mon Sep 17 00:00:00 2001 From: Carlos Tasada Date: Thu, 9 May 2024 14:05:09 +0200 Subject: [PATCH] Improved Long-Double Number Policy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Parsing of a Double value was always executing a `Long.parseLong(value)`, which generated a `NumberFormatException`. Identifying that a Number is a Double or a Long can be easily achieve (in a naive way) looking for the decimal separator. This simple change avoids the extra `NumberFormatException` A simple JUnit test, parsing a `Long` or a `Double` 10K times shows the next values: * Double (old parsing): ~42 ms * Double (new parsing): ~6 ms * Long (old parsing): ~7 ms * Long (new parsing): ~7 ms As we can see, the parsing for `Long` values stays the same (±1ms), while the parsing for `Double` is dramatically improved. Reducing the number of exceptions also has a positive side effect in memory consumption. --- .../java/com/google/gson/ToNumberPolicy.java | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/gson/src/main/java/com/google/gson/ToNumberPolicy.java b/gson/src/main/java/com/google/gson/ToNumberPolicy.java index 6380e5d986..0cd17ff3b7 100644 --- a/gson/src/main/java/com/google/gson/ToNumberPolicy.java +++ b/gson/src/main/java/com/google/gson/ToNumberPolicy.java @@ -68,22 +68,30 @@ public Number readNumber(JsonReader in) throws IOException { @Override public Number readNumber(JsonReader in) throws IOException, JsonParseException { String value = in.nextString(); - try { - return Long.parseLong(value); - } catch (NumberFormatException longE) { + if (value.contains(".")) { + return parseAsDouble(value, in); + } else { try { - Double d = Double.valueOf(value); - if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) { - throw new MalformedJsonException( - "JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath()); - } - return d; - } catch (NumberFormatException doubleE) { - throw new JsonParseException( - "Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE); + return Long.parseLong(value); + } catch (NumberFormatException longE) { + return parseAsDouble(value, in); } } } + + private Number parseAsDouble(String value, JsonReader in) throws IOException { + try { + Double d = Double.valueOf(value); + if ((d.isInfinite() || d.isNaN()) && !in.isLenient()) { + throw new MalformedJsonException( + "JSON forbids NaN and infinities: " + d + "; at path " + in.getPreviousPath()); + } + return d; + } catch (NumberFormatException doubleE) { + throw new JsonParseException( + "Cannot parse " + value + "; at path " + in.getPreviousPath(), doubleE); + } + } }, /**