Fixed serialization of Longs outside of IEEE 754 range. (#123)
* Fixed serialization of Longs outside of IEEE 754 range.
Signed-off-by: Roman Grigoriadi <roman.grigoriadi@oracle.com>
* Longs outside of IEEE 754 for range negative values.
Signed-off-by: Roman Grigoriadi <roman.grigoriadi@oracle.com>
* Test for JSONP number quotation.
Signed-off-by: Roman Grigoriadi <roman.grigoriadi@oracle.com>
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/BigNumberUtil.java b/src/main/java/org/eclipse/yasson/internal/serializer/BigNumberUtil.java
index 41ccc6a..5cb0724 100644
--- a/src/main/java/org/eclipse/yasson/internal/serializer/BigNumberUtil.java
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/BigNumberUtil.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 2018 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
@@ -26,6 +26,12 @@
// 53 means max bit value of number with sign bit included
private static final int MAX_BIT_SIZE = 53;
+ // Max value for Long. Similar to JavaScript Number.MAX_SAFE_INTEGER (2**53)-1
+ private static final long MAX_JS_SAFE_VALUE = 9007199254740991L;
+
+ // Min value for Long. Similar to JavaScript Number.MIN_SAFE_INTEGER -(2**53)+1
+ private static final long MIN_JS_SAFE_VALUE = -9007199254740991L;
+
// -1022 is the lowest range of the exponent
// more https://en.wikipedia.org/wiki/Exponent_bias
private static final int MIN_RANGE = -1022;
@@ -62,4 +68,16 @@
return value.abs().bitLength() <= MAX_BIT_SIZE;
}
+ /**
+ * Checks whether the value of {@link Long} matches format IEEE-754
+ *
+ * @param value value which is going to be checked
+ * @return true if value matches format IEEE-754
+ */
+ static boolean isIEEE754(Long value) {
+ return value >= MIN_JS_SAFE_VALUE && value <= MAX_JS_SAFE_VALUE;
+ }
+
+
+
}
diff --git a/src/main/java/org/eclipse/yasson/internal/serializer/LongTypeSerializer.java b/src/main/java/org/eclipse/yasson/internal/serializer/LongTypeSerializer.java
index 342841f..2d8fe91 100644
--- a/src/main/java/org/eclipse/yasson/internal/serializer/LongTypeSerializer.java
+++ b/src/main/java/org/eclipse/yasson/internal/serializer/LongTypeSerializer.java
@@ -35,11 +35,19 @@
@Override
protected void serializeNonFormatted(Long obj, JsonGenerator generator, String key) {
- generator.write(key, obj);
+ if (BigNumberUtil.isIEEE754(obj)) {
+ generator.write(key, obj);
+ } else {
+ generator.write(key, obj.toString());
+ }
}
@Override
protected void serializeNonFormatted(Long obj, JsonGenerator generator) {
- generator.write(obj);
+ if (BigNumberUtil.isIEEE754(obj)) {
+ generator.write(obj);
+ } else {
+ generator.write(obj.toString());
+ }
}
}
diff --git a/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java b/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
index ed21bba..2841f3f 100644
--- a/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
+++ b/src/test/java/org/eclipse/yasson/defaultmapping/basic/NumberTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2016, 2017 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
@@ -20,8 +20,14 @@
import org.junit.Assert;
import org.junit.Test;
+import javax.json.Json;
+import javax.json.JsonObject;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonWriter;
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
+import javax.json.stream.JsonGenerator;
+import java.io.StringWriter;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -120,4 +126,86 @@
jsonString = jsonb.toJson(new Object() { public Number number = new BigDecimal("0.1000000000000001"); });
Assert.assertEquals("{\"number\":0.1000000000000001}", jsonString);
}
+
+ @Test
+ public void testLongIEEE748() {
+
+ // 9007199254740991L
+ Long maxJsSafeValue = Double.valueOf(Math.pow(2, 53)).longValue() - 1;
+ Long upperJsUnsafeValue = maxJsSafeValue + 1;
+
+ String json = jsonb.toJson(maxJsSafeValue);
+ Assert.assertEquals("9007199254740991", json);
+ Long deserialized = jsonb.fromJson(json, Long.class);
+ Assert.assertEquals(Long.valueOf("9007199254740991"), deserialized);
+
+ json = jsonb.toJson(upperJsUnsafeValue);
+ Assert.assertEquals("\"9007199254740992\"", json);
+ deserialized = jsonb.fromJson(json, Long.class);
+ Assert.assertEquals(Long.valueOf("9007199254740992"), deserialized);
+
+
+ Long minJsSafeValue = Math.negateExact(maxJsSafeValue);
+ Long lowerJsUnsafeValue = minJsSafeValue - 1;
+
+ json = jsonb.toJson(minJsSafeValue);
+ Assert.assertEquals("-9007199254740991", json);
+ deserialized = jsonb.fromJson(json, Long.class);
+ Assert.assertEquals(Long.valueOf("-9007199254740991"), deserialized);
+
+ json = jsonb.toJson(lowerJsUnsafeValue);
+ Assert.assertEquals("\"-9007199254740992\"", json);
+ deserialized = jsonb.fromJson(json, Long.class);
+ Assert.assertEquals(Long.valueOf("-9007199254740992"), deserialized);
+ }
+
+ /**
+ * Tests that JSON-P RI itself does no big number (out of IEEE 754 quotation).
+ * This is why it is now must be done in Yasson to match the JSONB spec.
+ */
+ @Test
+ public void testJsonpBigNumber() {
+ StringWriter w = new StringWriter();
+ JsonGenerator generator = Json.createGenerator(w);
+
+ Long maxJsSafeValue = Double.valueOf(Math.pow(2, 53)).longValue() - 1;
+ Long upperJsUnsafeValue = Long.MAX_VALUE;
+
+ generator.writeStartObject();
+ generator.write("safeLongValue", maxJsSafeValue);
+ generator.write("unsafeLongValue", upperJsUnsafeValue);
+ generator.write("safeBigDecimalValue", BigDecimal.TEN);
+ generator.write("unsafeBigDecimalValue", BigDecimal.valueOf(upperJsUnsafeValue));
+ generator.writeEnd();
+ generator.close();
+
+ Assert.assertEquals("{" +
+ "\"safeLongValue\":9007199254740991," +
+ "\"unsafeLongValue\":9223372036854775807," +
+ "\"safeBigDecimalValue\":10," +
+ "\"unsafeBigDecimalValue\":9223372036854775807}",
+ w.toString());
+
+
+ w = new StringWriter();
+ JsonWriter writer = Json.createWriter(w);
+
+
+ JsonObjectBuilder objectBuilder = Json.createObjectBuilder();
+ objectBuilder.add("safeLongValue", maxJsSafeValue);
+ objectBuilder.add("unsafeLongValue", upperJsUnsafeValue);
+ objectBuilder.add("safeBigDecimalValue", BigDecimal.valueOf(maxJsSafeValue));
+ objectBuilder.add("unsafeBigDecimalValue", BigDecimal.valueOf(upperJsUnsafeValue));
+ JsonObject build = objectBuilder.build();
+ writer.write(build);
+ writer.close();
+
+ Assert.assertEquals("{" +
+ "\"safeLongValue\":9007199254740991," +
+ "\"unsafeLongValue\":9223372036854775807," +
+ "\"safeBigDecimalValue\":9007199254740991," +
+ "\"unsafeBigDecimalValue\":9223372036854775807}", w.toString());
+
+ }
+
}