Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,7 @@ public Object readLatin1(Latin1JsonReader reader) {
reader.readNull();
return null;
}
return reader.readNumber();
return reader.readNaturalNumber();
}

@Override
Expand All @@ -241,7 +241,7 @@ public Object readUtf16(Utf16JsonReader reader) {
reader.readNull();
return null;
}
return reader.readNumber();
return reader.readNaturalNumber();
}

@Override
Expand All @@ -259,7 +259,7 @@ public Object readUtf8(Utf8JsonReader reader) {
reader.readNull();
return null;
}
return reader.readNumber();
return reader.readNaturalNumber();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,19 @@
return materializeNumber(readNumberAsString());
}

/** Reads a natural JSON number without reducing decimal values to binary floating point. */
@Internal
public Number readNaturalNumber() {
skipWhitespace();
int start = position;
String number = readNumberAsString();
if (hasDecimalSeparator(number)) {
position = start;
return materializeNaturalDecimal(readBigDecimal());
}
return materializeNumber(number);
}

private String readNumberToken() {
int start = position;
scanNumberToken();
Expand Down Expand Up @@ -1905,6 +1918,26 @@
}
}

private static boolean hasDecimalSeparator(String number) {
int length = number.length();
for (int i = 0; i < length; i++) {
char ch = number.charAt(i);
if (ch == '.' || ch == 'e' || ch == 'E') {
return true;
}
}
return false;
}

protected static Number materializeNaturalDecimal(BigDecimal value) {
double asDouble = value.doubleValue();
// Keep the compact representation when it preserves the decimal value exactly.
if (Double.isFinite(asDouble) && BigDecimal.valueOf(asDouble).compareTo(value) == 0) {
return Double.valueOf(asDouble);
}
return value;
}

private byte[] bigNumberBytes(String number) {
if (StringSerializer.isBytesBackedString() && StringSerializer.getStringCoder(number) == 0) {
return StringSerializer.getStringBytes(number);
Expand Down Expand Up @@ -2013,7 +2046,7 @@
private static BigInteger bigInteger(int[] magnitude, int signum) {
try {
return (BigInteger) BIG_INTEGER_CONSTRUCTOR.invokeExact(magnitude, signum);
} catch (ThreadDeath e) {

Check warning on line 2049 in java/fory-json/src/main/java/org/apache/fory/json/reader/JsonReader.java

View workflow job for this annotation

GitHub Actions / JavaScript Xlang Test

java.lang.ThreadDeath in java.lang has been deprecated and marked for removal
throw e;
} catch (VirtualMachineError e) {
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,13 @@
return readIntegerToken(false);
}

@Override
@Internal
public Number readNaturalNumber() {
skipWhitespaceFast();
return readIntegerToken(false, true);
}

@Override
long scanNumberToken() {
byte[] bytes = input;
Expand Down Expand Up @@ -1094,6 +1101,10 @@
// BigInteger requires integer syntax; Number chooses compact Long storage and also accepts
// decimal suffixes. Share the validated prefix so representation selection does not rescan it.
private Number readIntegerToken(boolean integerOnly) {
return readIntegerToken(integerOnly, false);
}

private Number readIntegerToken(boolean integerOnly, boolean exactDecimal) {
byte[] bytes = input;
int limit = inputLimit;
int start = position;
Expand All @@ -1112,7 +1123,7 @@
if (!integerOnly && position < limit) {
int next = bytes[position];
if (next == '.' || next == 'e' || next == 'E') {
return readDecimalNumber(start);
return readDecimalNumber(start, exactDecimal);
}
}
rejectFractionOrExponentFast();
Expand Down Expand Up @@ -1160,13 +1171,13 @@
offset++;
}
if (offset < limit && bytes[offset] >= '0' && bytes[offset] <= '9') {
return readIntegerTail(integerOnly, start, offset, value);
return readIntegerTail(integerOnly, exactDecimal, start, offset, value);
}
position = offset;
if (!integerOnly && offset < limit) {
int next = bytes[offset];
if (next == '.' || next == 'e' || next == 'E') {
return readDecimalNumber(start);
return readDecimalNumber(start, exactDecimal);
}
}
rejectFractionOrExponentFast();
Expand All @@ -1177,22 +1188,26 @@
return integerOnly ? BigInteger.valueOf(signed) : Long.valueOf(signed);
}

private Number readIntegerTail(boolean integerOnly, int start, int offset, long prefix) {
private Number readIntegerTail(
boolean integerOnly, boolean exactDecimal, int start, int offset, long prefix) {
int end = scanNumberDigits(input, offset, inputLimit);
position = end;
if (!integerOnly && end < inputLimit) {
int next = input[end];
if (next == '.' || next == 'e' || next == 'E') {
return readDecimalNumber(start);
return readDecimalNumber(start, exactDecimal);
}
}
rejectFractionOrExponentFast();
return parseBigInteger(input, start, end, prefix, offset);
}

private Double readDecimalNumber(int start) {
// Preserve Number's Double representation and JDK conversion for points and exponents.
private Number readDecimalNumber(int start, boolean exact) {
position = start;
if (exact) {
return materializeNaturalDecimal(readBigDecimal());
}
// Preserve Number's Double representation and JDK conversion for points and exponents.
return Double.valueOf(Double.parseDouble(readNumberAsString()));
}

Expand Down Expand Up @@ -2788,7 +2803,7 @@
}
try {
return (Instant) INSTANT_FACTORY.invokeExact(seconds, nano);
} catch (ThreadDeath e) {

Check warning on line 2806 in java/fory-json/src/main/java/org/apache/fory/json/reader/Utf8JsonReader.java

View workflow job for this annotation

GitHub Actions / JavaScript Xlang Test

java.lang.ThreadDeath in java.lang has been deprecated and marked for removal
throw e;
} catch (VirtualMachineError e) {
throw e;
Expand Down Expand Up @@ -3197,7 +3212,7 @@
}
try {
return (ZonedDateTime) ZONED_DATE_TIME_CONSTRUCTOR.invokeExact(dateTime, offset, zone);
} catch (ThreadDeath | VirtualMachineError e) {

Check warning on line 3215 in java/fory-json/src/main/java/org/apache/fory/json/reader/Utf8JsonReader.java

View workflow job for this annotation

GitHub Actions / JavaScript Xlang Test

java.lang.ThreadDeath in java.lang has been deprecated and marked for removal
throw e;
} catch (Throwable e) {
throw new ForyJsonException("Cannot construct JSON zoned date-time", e);
Expand Down Expand Up @@ -3269,7 +3284,7 @@
}
try {
return (YearMonth) YEAR_MONTH_CONSTRUCTOR.invokeExact(year, month);
} catch (ThreadDeath e) {

Check warning on line 3287 in java/fory-json/src/main/java/org/apache/fory/json/reader/Utf8JsonReader.java

View workflow job for this annotation

GitHub Actions / JavaScript Xlang Test

java.lang.ThreadDeath in java.lang has been deprecated and marked for removal
throw e;
} catch (VirtualMachineError e) {
throw e;
Expand Down Expand Up @@ -3321,7 +3336,7 @@
}
try {
return (MonthDay) MONTH_DAY_CONSTRUCTOR.invokeExact(month, day);
} catch (ThreadDeath e) {

Check warning on line 3339 in java/fory-json/src/main/java/org/apache/fory/json/reader/Utf8JsonReader.java

View workflow job for this annotation

GitHub Actions / JavaScript Xlang Test

java.lang.ThreadDeath in java.lang has been deprecated and marked for removal
throw e;
} catch (VirtualMachineError e) {
throw e;
Expand Down Expand Up @@ -3630,7 +3645,7 @@
}
try {
return (LocalDate) LOCAL_DATE_FACTORY.invokeExact(year, month, day);
} catch (ThreadDeath e) {

Check warning on line 3648 in java/fory-json/src/main/java/org/apache/fory/json/reader/Utf8JsonReader.java

View workflow job for this annotation

GitHub Actions / JavaScript Xlang Test

java.lang.ThreadDeath in java.lang has been deprecated and marked for removal
throw e;
} catch (VirtualMachineError e) {
throw e;
Expand Down Expand Up @@ -3704,7 +3719,7 @@
}
try {
return (LocalTime) LOCAL_TIME_FACTORY.invokeExact(hour, minute, second, nano);
} catch (ThreadDeath e) {

Check warning on line 3722 in java/fory-json/src/main/java/org/apache/fory/json/reader/Utf8JsonReader.java

View workflow job for this annotation

GitHub Actions / JavaScript Xlang Test

java.lang.ThreadDeath in java.lang has been deprecated and marked for removal
throw e;
} catch (VirtualMachineError e) {
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,24 @@ public void readJsonContainers() {
assertTrue(((JsonObject) natural).get("items") instanceof JsonArray);
}

@Test
public void preserveNaturalDecimalPrecision() {
ForyJson json = newJson();
String literal = "1.234567890123456789012345678901234567890";

Object value = json.fromJson(literal, Object.class);

assertEquals(value, new BigDecimal(literal));
assertEquals(value.getClass(), BigDecimal.class);
assertEquals(
json.fromJson(literal.getBytes(StandardCharsets.UTF_8), Object.class),
new BigDecimal(literal));
assertEquals(json.fromJson("1e400", Object.class), new BigDecimal("1e400"));
assertEquals(
json.fromJson("123456789012345678901234567890", Object.class),
new BigInteger("123456789012345678901234567890"));
}

@Test
public void parsedContainersStartSmall() {
ForyJson json = newJson();
Expand Down
Loading