-
Notifications
You must be signed in to change notification settings - Fork 545
feat: support serializing non-finite numbers as strings, for issue #3911 #3923
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: android5
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds support for serializing non-finite floating-point numbers (NaN, Infinity, -Infinity) as strings instead of null values. A new feature flag WriteFloatSpecialAsString is introduced to enable this behavior. When enabled, NaN is serialized as "NaN", positive infinity as "Infinity", and negative infinity as "-Infinity".
Key Changes
- Added
WriteFloatSpecialAsStringfeature flag toJSONWriter.Featureenum - Extended
DoubleToDecimal.toString()methods to accept awriteSpecialAsStringparameter - Updated float/double serialization logic in
JSONWriterUTF8andJSONWriterUTF16to conditionally serialize special values as quoted strings
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| core/src/main/java/com/alibaba/fastjson2/JSONWriter.java | Adds new WriteFloatSpecialAsString feature flag and integrates it into format handling methods |
| core/src/main/java/com/alibaba/fastjson2/util/DoubleToDecimal.java | Extends all toString() method signatures with writeSpecialAsString parameter and implements logic to wrap NaN/Infinity in quotes when enabled |
| core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java | Updates writeFloat(), writeDouble(), and array methods to use the new feature flag and pass it to DoubleToDecimal |
| core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java | Updates writeFloat(), writeDouble(), writeDoubleArray(), and array methods to use the new feature flag |
| core/src/main/java/com/alibaba/fastjson2/util/TypeUtils.java | Updates toBigDecimal() calls to pass false for the new writeSpecialAsString parameter |
| core/src/test/java/com/alibaba/fastjson2/features/WriteFloatSpecialAsString.java | Comprehensive test suite covering UTF-8, UTF-16, arrays, reflection, and interaction with other features |
| core/src/test/java/com/alibaba/fastjson2/v1issues/basicType/DoubleTest3_random.java | Updates test call to include new writeSpecialAsString parameter |
| core/src/test/java/com/alibaba/fastjson2/util/RyuTest.java | Updates test helper methods to pass false for new parameter |
| core/src/test/java/com/alibaba/fastjson2/util/RyuFloatTest.java | Updates test helper method to pass false for new parameter |
| core/src/test/java/com/alibaba/fastjson2/util/RyuDoubleTest.java | Updates test helper method to pass false for new parameter |
| core/src/test/java/com/alibaba/fastjson2/util/FloatToDecimalTest.java | Updates test calls to pass false for new parameter |
| core/src/test/java/com/alibaba/fastjson2/util/DoubleToDecimalTest.java | Updates test calls to pass false for new parameter |
| benchmark/src/main/java/com/alibaba/fastjson2/benchmark/fastcode/DoubleToString.java | Updates benchmark methods to pass false for new parameter |
Comments suppressed due to low confidence (2)
core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java:2285
- The minCapacity calculation doesn't account for the additional space needed when writing special values as strings. When
WriteFloatSpecialAsStringis enabled, NaN becomes "NaN" (5 chars), Infinity becomes "Infinity" (10 chars), and negative infinity becomes "-Infinity" (11 chars). The current calculation ofoff + 24may be insufficient for doubles. It should add extra capacity whenwriteSpecialAsStringis true.
int minCapacity = off + 24;
if (writeAsString) {
minCapacity += 2;
}
core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java:2213
- The minCapacity calculation doesn't account for the additional space needed when writing special values as strings. When
WriteFloatSpecialAsStringis enabled, NaN becomes "NaN" (5 chars), Infinity becomes "Infinity" (10 chars), and negative infinity becomes "-Infinity" (11 chars). The current calculation ofoff + 15may be insufficient. It should add extra capacity whenwriteSpecialAsStringis true, similar to how it's done forwriteAsString.
int minCapacity = off + 15;
if (writeAsString) {
minCapacity += 2;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| boolean writeSpecialAsString = (context.features & WriteFloatSpecialAsString.mask) != 0; | ||
|
|
||
| int off = this.off; | ||
| int minCapacity = off + values.length * (writeAsString ? 16 : 18) + 1; |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The minCapacity calculation may be insufficient when WriteFloatSpecialAsString is enabled. The calculation uses values.length * (writeAsString ? 16 : 18) but this doesn't account for special string values which require up to 11 characters ("-Infinity"). When writeSpecialAsString is true, the capacity should be increased to accommodate the larger special strings.
| int minCapacity = off + values.length * (writeAsString ? 16 : 18) + 1; | |
| int perValueCapacity = writeAsString ? 16 : 18; | |
| if (writeSpecialAsString) { | |
| perValueCapacity += 4; // additional space for special values like "-Infinity" | |
| } | |
| int minCapacity = off + values.length * perValueCapacity + 1; |
| boolean writeSpecialAsString = (context.features & WriteFloatSpecialAsString.mask) != 0; | ||
|
|
||
| int off = this.off; | ||
| int minCapacity = off + values.length * 27 + 1; |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The minCapacity calculation may be insufficient when WriteFloatSpecialAsString is enabled. The calculation uses values.length * 27 but this doesn't account for special string values which require up to 11 characters ("-Infinity"). When writeSpecialAsString is true, the capacity should be increased to accommodate the larger special strings.
| int minCapacity = off + values.length * 27 + 1; | |
| int charsPerValue = 27; | |
| if (writeSpecialAsString) { | |
| // Allow extra space for special string representations like "-Infinity" | |
| charsPerValue += 11; | |
| } | |
| int minCapacity = off + values.length * charsPerValue + 1; |
| boolean writeSpecialAsString = (context.features & WriteFloatSpecialAsString.mask) != 0; | ||
|
|
||
| int off = this.off; | ||
| int minCapacity = off + values.length * 27 + 1; |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The minCapacity calculation may be insufficient when WriteFloatSpecialAsString is enabled. The calculation uses values.length * 27 but this doesn't account for special string values which require up to 11 bytes ("-Infinity"). When writeSpecialAsString is true, the capacity should be increased to accommodate the larger special strings.
| int minCapacity = off + values.length * 27 + 1; | |
| int charsPerValue = 27; | |
| if (writeAsString || writeSpecialAsString) { | |
| charsPerValue = 32; | |
| } | |
| int minCapacity = off + values.length * charsPerValue + 1; |
| WriterUtilDateAsMillis(1L << 44), | ||
|
|
||
| /** | ||
| * Feature that determines whether to write float/double NaN and Infinite values as Strings. |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment mentions "Infinite" but should say "Infinity" to match the actual serialized values ("Infinity" and "-Infinity") produced by the implementation.
| * Feature that determines whether to write float/double NaN and Infinite values as Strings. | |
| * Feature that determines whether to write float/double NaN and Infinity values as Strings. |
| * @since 2.0.54 | ||
| */ | ||
| PrettyFormatWith4Space(1L << 43), | ||
|
|
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The WriterUtilDateAsMillis feature on line 2154 is missing documentation. For consistency with other features in the enum, it should have a JavaDoc comment explaining its purpose and including a @SInCE tag.
| /** | |
| * Feature that determines whether {@link java.util.Date} values are written | |
| * as milliseconds from the epoch instead of using the default date format. | |
| * @since 2.0.60 | |
| */ |
| if (writeAsString) { | ||
| minCapacity += 2; | ||
| minCapacity += 4; | ||
| } |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The minCapacity calculation may be insufficient when WriteFloatSpecialAsString is enabled. The calculation assumes 48 chars for two doubles, but with special values as strings, "-Infinity" requires 11 characters. The capacity should account for the maximum special string length when writeSpecialAsString is true.
| } | |
| } | |
| if (writeSpecialAsString) { | |
| // Account for maximum length of special double string representations (e.g., "-Infinity") | |
| // for both elements in the array. | |
| minCapacity += 22; // 2 * 11 | |
| } |
| } | ||
|
|
||
| int off = this.off; | ||
| int minCapacity = off + 17; |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The minCapacity calculation doesn't account for the additional space needed when writing special values as strings. When WriteFloatSpecialAsString is enabled, "-Infinity" requires 11 bytes with quotes. The current calculation of off + 17 may be insufficient. It should account for the maximum special string length when writeSpecialAsString is true.
| int minCapacity = off + 17; | |
| int additionalCapacity = 17; | |
| if (writeSpecialAsString) { | |
| // Ensure enough space for the longest special float string representation (e.g. "-Infinity") | |
| // including potential surrounding quotes when written as a string. | |
| additionalCapacity = 21; | |
| } | |
| int minCapacity = off + additionalCapacity; |
| int off = this.off; | ||
| int minCapacity = off + 26; |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The minCapacity calculation doesn't account for the additional space needed when writing special values as strings. When WriteFloatSpecialAsString is enabled, "-Infinity" requires 11 bytes with quotes. The current calculation of off + 26 may be insufficient for doubles. It should account for the maximum special string length when writeSpecialAsString is true.
| boolean writeSpecialAsString = (context.features & WriteFloatSpecialAsString.mask) != 0; | ||
|
|
||
| int off = this.off; | ||
| int minCapacity = off + values.length * (writeAsString ? 16 : 18) + 1; |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The minCapacity calculation may be insufficient when WriteFloatSpecialAsString is enabled. The calculation uses values.length * (writeAsString ? 16 : 18) but this doesn't account for special string values which require up to 11 bytes ("-Infinity"). When writeSpecialAsString is true, the capacity should be increased to accommodate the larger special strings.
| int minCapacity = off + values.length * (writeAsString ? 16 : 18) + 1; | |
| int perValueCapacity = writeAsString ? 16 : 18; | |
| if (writeSpecialAsString && perValueCapacity < 11) { | |
| // Ensure enough space for special values like "-Infinity" (up to 11 bytes) | |
| perValueCapacity = 11; | |
| } | |
| int minCapacity = off + values.length * perValueCapacity + 1; |
What this PR does / why we need it?
android5 分支
Summary of your change
Please indicate you've done the following: