From 6dbdb272c0950d1bd17c3e3b5e38b385d1373eb2 Mon Sep 17 00:00:00 2001 From: Inderjeet Singh Date: Thu, 18 Dec 2008 23:41:44 +0000 Subject: [PATCH] Added checks to ensure that we do not serialize NaN or postiive or negative infinity for floats. --- gson/JsonParser.jj | 236 ++++++++++++++++++ gson/pom.xml | 4 +- .../com/google/gson/DefaultTypeAdapters.java | 3 + .../google/gson/functional/PrimitiveTest.java | 87 ++++++- 4 files changed, 322 insertions(+), 8 deletions(-) create mode 100644 gson/JsonParser.jj diff --git a/gson/JsonParser.jj b/gson/JsonParser.jj new file mode 100644 index 00000000..7c4e80df --- /dev/null +++ b/gson/JsonParser.jj @@ -0,0 +1,236 @@ +/** + * Adapted from the Json parser grammar from http://code.google.com/p/jsonparser/ + * + * Author: Inderjeet Singh + */ + +options { + STATIC = false; + UNICODE_INPUT = true; +} + +PARSER_BEGIN(JsonParser) + +package com.google.gson; + +@SuppressWarnings("all") +final class JsonParser { + +} + +PARSER_END(JsonParser) + +SKIP : { " " | "\t" | "\n" | "\r" } +/* + * Technically Json does not allow leading zeros in numbers, but we + * will allow that. + */ +TOKEN : { + + | + | + | + | + | + | <#HEX_CHAR : ["a"-"f","A"-"F","0"-"9"]> + | > + | <#ESCAPE_CHAR: "\\" ["n","t","b","r","f","\\","'","\"", "/"] > + | | )* "\'" > + | | )* "\"" > + | : STRING_STATE +} + MORE : { "\\" : ESC_STATE } + TOKEN : { + > : DEFAULT +| +} + TOKEN : { + : STRING_STATE +} + MORE : { "u" : HEX_STATE } + TOKEN : { + <#HEX : ["a"-"f","A"-"F","0"-"9"]> +| > : STRING_STATE +} + +public JsonElement parse() : +{ + JsonElement json = null; +} +{ + ( json=JsonObject() | + json=JsonArray() | + json=JsonPrimitive() | + json=JsonNull()) + { return json; } +} + +private JsonObject JsonObject() : +{ + JsonObject o = new JsonObject(); +} +{ + "{" [ Members(o) ] "}" + { return o; } +} + +private JsonNull JsonNull() : +{ + Token t; +} +{ + t = { return new JsonNull(); } +} + +private void Members(JsonObject o) : +{ } +{ + Pair(o) [ "," Members(o) ] +} + +private void Pair(JsonObject o) : +{ + JsonPrimitive property; + JsonElement value; +} +{ + property=JsonMemberName() ":" value=JsonValue() + { + o.add(property.getAsString(), value); + } +} + +private JsonPrimitive JsonMemberName() : +{ Token t; JsonPrimitive value; } +{ + t= { return new JsonPrimitive(t.image); } | + value=JsonString() { return value; } +} + +private JsonArray JsonArray() : +{ JsonArray array = new JsonArray(); } +{ + "[" [ Elements(array) ] "]" + { + array.reverse(); + return array; + } +} + +private void Elements(JsonArray array) : +{ + JsonElement element; +} +{ + element=JsonValue() [ "," Elements(array) ] + { array.add(element); } +} + +private JsonElement JsonValue() : +{ JsonElement o = null; } +{ +( o=JsonString() | + o=JsonNumber() | + o=JsonObject() | + o=JsonArray() | + o=JsonBoolean() | + o=JsonNull() ) + { return o; } +} + +private JsonPrimitive JsonBoolean() : +{ Token t; } +{ + t= { + boolean value = Boolean.valueOf(t.image); + return new JsonPrimitive(value); + } +} + +private JsonPrimitive JsonPrimitive() : +{ + JsonPrimitive value; +} +{ + ( value=JsonString()) { return value; } | + ( value=JsonNumber()) { return value; } | + ( value=JsonBoolean()) { return value; } +} + +private JsonPrimitive JsonNumber() : +{ + JsonPrimitive value; + String intpart = null, + fracpart = null, + exppart = null; +} +{ + (value=JsonNaN()) {return value; } | + (intpart=JsonInt() [ fracpart=JsonFrac() ] [ exppart=JsonExp() ]) + { + Number n; + if (exppart != null || fracpart != null) { + fracpart = (fracpart == null) ? "" : fracpart; + exppart = (exppart == null) ? "" : exppart; + n = new java.math.BigDecimal(intpart + fracpart + exppart); + } else { + n = new java.math.BigInteger(intpart); + } + return new JsonPrimitive(n); + } +} + +private JsonPrimitive JsonNaN() : +{ + Token t; +} +{ + t= {return new JsonPrimitive(Double.NaN); } +} + +private String JsonInt() : +{ + String digits; + boolean negative = false; +} +{ + ["-" { negative = true; } ] digits=Digits() + { + if(negative) + return "-" + digits; + return digits; + } +} + +private String JsonFrac() : +{ String digits; } +{ + "." digits=Digits() + { return "." + digits; } +} + +private String JsonExp() : +{ + Token t; + String digits; +} +{ + t= digits=Digits() + { return t.image + digits; } +} + +private String Digits() : +{ Token t; } +{ + t= + { return t.image; } +} + +private JsonPrimitive JsonString() : +{ Token t; } +{ + (t= | t=) { + String value = StringUnmarshaller.unmarshall(t.image); + return new JsonPrimitive(value); + } +} diff --git a/gson/pom.xml b/gson/pom.xml index 0096988d..f8f6c07c 100644 --- a/gson/pom.xml +++ b/gson/pom.xml @@ -118,7 +118,7 @@ - + org.apache.maven.plugins maven-jxr-plugin diff --git a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java index 158469cc..dbdaca96 100644 --- a/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java +++ b/gson/src/main/java/com/google/gson/DefaultTypeAdapters.java @@ -643,6 +643,9 @@ final class DefaultTypeAdapters { private static class FloatTypeAdapter implements InstanceCreator, JsonSerializer, JsonDeserializer { public JsonElement serialize(Float src, Type typeOfSrc, JsonSerializationContext context) { + if (Float.isNaN(src) || Float.isInfinite(src)) { + throw new IllegalArgumentException(src + " is not a valid double value as per JavaScript specification."); + } return new JsonPrimitive(src); } 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 7ccdd80f..6dc8ce97 100644 --- a/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java +++ b/gson/src/test/java/com/google/gson/functional/PrimitiveTest.java @@ -349,44 +349,96 @@ public class PrimitiveTest extends TestCase { return json.substring(json.indexOf('[') + 1, json.indexOf(']')); } - public void testNaNSerializationNotSupported() { + public void testDoubleNaNSerializationNotSupported() { try { double d = Double.NaN; gson.toJson(d); + Double dw = Double.NaN; + gson.toJson(dw); fail("Gson should not accept NaN for serialization"); } catch (IllegalArgumentException expected) { } } - public void testNaNDeserializationNotSupported() { + public void testDoubleNaNDeserializationNotSupported() { try { String json = "NaN"; assertEquals(Double.NaN, gson.fromJson(json, Double.class)); + assertEquals(Double.NaN, gson.fromJson(json, double.class)); + fail("Gson should not accept NaN for deserialization"); } catch (JsonParseException expected) { } - } + } - public void testInfinitySerializationNotSupported() { + public void testFloatNaNSerializationNotSupported() { + try { + float f = Float.NaN; + gson.toJson(f); + Float fw = Float.NaN; + gson.toJson(fw); + fail("Gson should not accept NaN for serialization"); + } catch (IllegalArgumentException expected) { + } + } + + public void testFloatNaNDeserializationNotSupported() { + try { + String json = "NaN"; + assertEquals(Float.NaN, gson.fromJson(json, Float.class)); + assertEquals(Float.NaN, gson.fromJson(json, float.class)); + fail("Gson should not accept NaN for deserialization"); + } catch (JsonParseException expected) { + } + } + + public void testDoubleInfinitySerializationNotSupported() { try { double d = Double.POSITIVE_INFINITY; gson.toJson(d); + Double dw = Double.POSITIVE_INFINITY; + gson.toJson(dw); fail("Gson should not accept positive infinity for serialization"); } catch (IllegalArgumentException expected) { } } - public void testInfinityDeserializationNotSupported() { + public void testDoubleInfinityDeserializationNotSupported() { try { String json = "Infinity"; assertEquals(Double.POSITIVE_INFINITY, gson.fromJson(json, Double.class)); + assertEquals(Double.POSITIVE_INFINITY, gson.fromJson(json, double.class)); + fail("Gson should not accept positive infinity for deserialization"); } catch (JsonParseException expected) { } - } + } + + public void testFloatInfinitySerializationNotSupported() { + try { + float f = Float.POSITIVE_INFINITY; + gson.toJson(f); + Float fw = Float.POSITIVE_INFINITY; + gson.toJson(fw); + fail("Gson should not accept positive infinity for serialization"); + } catch (IllegalArgumentException expected) { + } + } + + public void testFloatInfinityDeserializationNotSupported() { + try { + String json = "Infinity"; + assertEquals(Float.POSITIVE_INFINITY, gson.fromJson(json, Float.class)); + assertEquals(Float.POSITIVE_INFINITY, gson.fromJson(json, float.class)); + fail("Gson should not accept positive infinity for deserialization"); + } catch (JsonParseException expected) { + } + } public void testNegativeInfinitySerializationNotSupported() { try { double d = Double.NEGATIVE_INFINITY; gson.toJson(d); + Double dw = Double.NEGATIVE_INFINITY; + gson.toJson(dw); fail("Gson should not accept positive infinity for serialization"); } catch (IllegalArgumentException expected) { } @@ -395,7 +447,30 @@ public class PrimitiveTest extends TestCase { public void testNegativeInfinityDeserializationNotSupported() { try { String json = "-Infinity"; + assertEquals(Double.NEGATIVE_INFINITY, gson.fromJson(json, double.class)); assertEquals(Double.NEGATIVE_INFINITY, gson.fromJson(json, Double.class)); + fail("Gson should not accept positive infinity for serialization"); + } catch (JsonParseException expected) { + } + } + + public void testNegativeInfinityFloatSerializationNotSupported() { + try { + float f = Float.NEGATIVE_INFINITY; + gson.toJson(f); + Float fw = Float.NEGATIVE_INFINITY; + gson.toJson(fw); + fail("Gson should not accept positive infinity for serialization"); + } catch (IllegalArgumentException expected) { + } + } + + public void testNegativeInfinityFloatDeserializationNotSupported() { + try { + String json = "-Infinity"; + assertEquals(Float.NEGATIVE_INFINITY, gson.fromJson(json, float.class)); + assertEquals(Float.NEGATIVE_INFINITY, gson.fromJson(json, Float.class)); + fail("Gson should not accept positive infinity for serialization"); } catch (JsonParseException expected) { } }