Skip to content

JsonBsonEncoder: fix parsing of JsonPrimitive numbers#1937

Open
berlix wants to merge 2 commits intomongodb:mainfrom
berlix:fix-json-primitive-numbers
Open

JsonBsonEncoder: fix parsing of JsonPrimitive numbers#1937
berlix wants to merge 2 commits intomongodb:mainfrom
berlix:fix-json-primitive-numbers

Conversation

@berlix
Copy link
Copy Markdown

@berlix berlix commented Apr 9, 2026

encodeJsonPrimitive would in some cases attempt to parse scientifically formatted numbers as plain Ints/Longs, which would result in a NumberFormatException.

The new test case testJsonPrimitiveNumberEncoding reproduces the issue/verifies the fix.

Note that the adjusted implementation always encodes numbers with the least amount of decimals necessary, which is why some other test cases needed to be adjusted.

JAVA-6165

encodeJsonPrimitive would in some cases attempt to parse scientifically formatted numbers as plain Ints/Longs, which would result in a NumberFormatException.
@berlix berlix requested a review from a team as a code owner April 9, 2026 14:04
@berlix berlix requested a review from stIncMale April 9, 2026 14:04
@rozza rozza requested a review from Copilot April 13, 2026 14:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes numeric handling in JsonBsonEncoder.encodeJsonPrimitive so scientifically formatted JSON numbers aren’t incorrectly parsed as Int/Long (avoiding NumberFormatException), and updates/extends tests to cover the corrected behavior and the new “minimal decimals” encoding.

Changes:

  • Normalize numeric JsonPrimitive values via BigDecimal(...).stripTrailingZeros() and use BigDecimal.toInt()/toLong() for integer-range encoding.
  • Update existing JSON element expectations to match the new normalized numeric output (e.g., 1E+19, 3.123E+700, non-.0 doubles).
  • Add a parameterized test that exercises scientific notation and large-number cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/JsonBsonEncoder.kt Adjusts numeric parsing/branching for JsonPrimitive numbers to properly handle scientific notation and normalized integer detection.
bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt Adds coverage for scientific-notation number encoding and updates expected JSON outputs to match normalized formatting.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +106 to 111
decimal.scale() > 0 ->
if (DOUBLE_MIN_VALUE <= decimal && decimal <= DOUBLE_MAX_VALUE) {
encodeDouble(primitive.double)
} else {
writer.writeDecimal128(Decimal128(decimal))
}
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The double-range check for fractional numbers only allows values >= Double.MIN_VALUE, which is positive. This causes negative fractional values (e.g., -1.1) to skip the double branch and be encoded as Decimal128 instead of a BSON double. Consider checking the absolute value (and treating 0 as a valid double) while still enforcing the finite double range, e.g., using decimal.abs() against Double.MIN_VALUE/Double.MAX_VALUE (and decimal == 0 special-case).

Suggested change
decimal.scale() > 0 ->
if (DOUBLE_MIN_VALUE <= decimal && decimal <= DOUBLE_MAX_VALUE) {
encodeDouble(primitive.double)
} else {
writer.writeDecimal128(Decimal128(decimal))
}
decimal.scale() > 0 -> {
val absoluteDecimal = decimal.abs()
if (decimal.signum() == 0 || (DOUBLE_MIN_VALUE <= absoluteDecimal && absoluteDecimal <= DOUBLE_MAX_VALUE)) {
encodeDouble(primitive.double)
} else {
writer.writeDecimal128(Decimal128(decimal))
}
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 817845e, with appropriate test cases. I removed the check for DOUBLE_MIN_VALUE entirely, since it wouldn't make a ton of sense - if anything, we'd want to switch to Decimal128's precision whenever Double cannot represent the original number within some (arbitrary) precision (say, max. 1% deviation), and not only when the number happens to be close to zero. But that seems like an inappropriate amount of guesswork to add here.

For the record, this issue was not introduced by my PR. :-)

import org.bson.codecs.kotlinx.samples.ValueClass
import org.bson.json.JsonMode
import org.bson.json.JsonWriterSettings
import org.bson.types.Decimal128
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decimal128 is imported but not referenced in this test file (the code uses asDecimal128() which returns BsonDecimal128). This will be flagged as an unused import by static analysis; please remove it.

Suggested change
import org.bson.types.Decimal128

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed as part of 817845e.

Comment on lines +227 to +238
"""{"value": 0}""" to """{"value": 0}""",
"""{"value": 0}""" to """{"value": 0.0}""",
"""{"value": 1.1}""" to """{"value": 1.1E0}""",
"""{"value": 11}""" to """{"value": 1.1E1}""",
"""{"value": 110}""" to """{"value": 1.1E2}""",
"""{"value": 1100}""" to """{"value": 1.1E3}""",
"""{"value": 0.1}""" to """{"value": 1E-1}""",
"""{"value": 0.01}""" to """{"value": 1E-2}""",
"""{"value": 0.001}""" to """{"value": 1E-3}""",
"""{"value": 35485464}""" to """{"value": 35485464}""",
"""{"value": 35485464}""" to """{"value": 35485464.0}""",
"""{"value": {"${'$'}numberDecimal": "123456789123456789123456789"}}""" to """{"value": 123456789123456789123456789}"""
Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The newly added testJsonPrimitiveNumberEncoding() MethodSource data is not formatted consistently (indentation is uneven across elements). Since the repo uses Spotless/ktfmt, this is likely to fail spotlessCheck; please reformat this block (e.g., run ./gradlew spotlessApply) so the Stream.of(...) entries are properly aligned.

Suggested change
"""{"value": 0}""" to """{"value": 0}""",
"""{"value": 0}""" to """{"value": 0.0}""",
"""{"value": 1.1}""" to """{"value": 1.1E0}""",
"""{"value": 11}""" to """{"value": 1.1E1}""",
"""{"value": 110}""" to """{"value": 1.1E2}""",
"""{"value": 1100}""" to """{"value": 1.1E3}""",
"""{"value": 0.1}""" to """{"value": 1E-1}""",
"""{"value": 0.01}""" to """{"value": 1E-2}""",
"""{"value": 0.001}""" to """{"value": 1E-3}""",
"""{"value": 35485464}""" to """{"value": 35485464}""",
"""{"value": 35485464}""" to """{"value": 35485464.0}""",
"""{"value": {"${'$'}numberDecimal": "123456789123456789123456789"}}""" to """{"value": 123456789123456789123456789}"""
"""{"value": 0}""" to """{"value": 0}""",
"""{"value": 0}""" to """{"value": 0.0}""",
"""{"value": 1.1}""" to """{"value": 1.1E0}""",
"""{"value": 11}""" to """{"value": 1.1E1}""",
"""{"value": 110}""" to """{"value": 1.1E2}""",
"""{"value": 1100}""" to """{"value": 1.1E3}""",
"""{"value": 0.1}""" to """{"value": 1E-1}""",
"""{"value": 0.01}""" to """{"value": 1E-2}""",
"""{"value": 0.001}""" to """{"value": 1E-3}""",
"""{"value": 35485464}""" to """{"value": 35485464}""",
"""{"value": 35485464}""" to """{"value": 35485464.0}""",
"""{"value": {"${'$'}numberDecimal": "123456789123456789123456789"}}""" to """{"value": 123456789123456789123456789}"""

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed as part of 817845e.

Also fixes formatting in KotlinSerializerCodecTest.kt.
@rozza rozza requested review from rozza and removed request for stIncMale April 14, 2026 09:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants