Skip to content

Conversation

@asolntsev
Copy link
Contributor

@asolntsev asolntsev commented Jan 23, 2026

User description

  1. newInput("42e-1").nextNumber() returned 4 (expected: 4.2);
  2. new Json().toType("42e-1", Double.class) returned 4.0 (expected: 4.2)

🔗 Related Issues

This is an alternative implementation for problem #16961

💥 What does this PR do?

Fixes parsing numeric values with exponents in JSON.

  1. newInput("42e-1").nextNumber()
    • was: 4
    • now: 4.2
  2. new Json().toType("42e-1", Double.class)
    • was: 5.0
    • now: 4.2

🔄 Types of changes

  • Bug fix (backwards compatible)

@joerg1985 What do you think?


PR Type

Bug fix


Description

  • Fixes JSON number parsing with scientific notation (exponents)

  • Detects fractional results from exponent calculations

  • Returns appropriate type (long vs double) based on result

  • Adds comprehensive test coverage for exponent scenarios


Diagram Walkthrough

flowchart LR
  A["JSON Number Input<br/>e.g. 42e-1"] --> B["Parse to BigDecimal"]
  B --> C["Check for Fractional Part<br/>or Exponent Result"]
  C --> D1["Has Fraction:<br/>Return Double"]
  C --> D2["No Fraction:<br/>Return Long"]
  D1 --> E["Correct Values<br/>42e-1 = 4.2"]
  D2 --> E
Loading

File Walkthrough

Relevant files
Bug fix
JsonInput.java
Add exponent result detection to number parsing                   

java/src/org/openqa/selenium/json/JsonInput.java

  • Modified nextNumber() to detect fractional results from exponent
    calculations
  • Added check: number.stripTrailingZeros().scale() > 0 to identify
    decimals
  • Returns doubleValue() for fractional results, longValue() for integers
  • Fixes incorrect parsing of numbers like "42e-1" (was 4, now 4.2)
+4/-3     
Tests
JsonInputTest.java
Add exponent parsing tests to JsonInputTest                           

java/test/org/openqa/selenium/json/JsonInputTest.java

  • Added test for non-decimal numbers with positive exponent (42e+1 =
    420L)
  • Added test for decimal numbers with negative exponent (42e-1 = 4.2)
  • Verifies correct type conversion (long vs double) based on result
+17/-0   
JsonTest.java
Add exponent conversion tests to JsonTest                               

java/test/org/openqa/selenium/json/JsonTest.java

  • Added tests for Json.toType() with various exponent formats
  • Tests positive exponents: 4.2e+1 and 42e+1 with multiple target types
  • Tests negative exponent: 42e-1 and 4.2e-1 returning correct double
    values
  • Validates type conversion (Double, Integer, Long) for exponent results
+11/-0   

1. `newInput("42e-1").nextNumber()` returned 4 (expected: 4.2);
2. `new Json().toType("42e-1", Double.class)` returned 4.0 (expected: 4.2)
@asolntsev asolntsev self-assigned this Jan 23, 2026
@selenium-ci selenium-ci added the C-java Java Bindings label Jan 23, 2026
@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 23, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status:
Non-descriptive variable: The new test introduces a single-letter variable x that is not a conventional short-lived
iterator and does not describe intent.

Referred Code
  Number x = input.nextNumber();
  assertThat((Double) x).isEqualTo(4.2d);
}

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 23, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Prevent silent integer overflow with longValueExact
Suggestion Impact:The commit replaced number.longValue() with number.longValueExact(), addressing the silent overflow concern. However, it did not add the suggested try/catch fallback to double on ArithmeticException.

code diff:

@@ -244,7 +244,7 @@
       if (fractionalPart || number.stripTrailingZeros().scale() > 0) {
         return number.doubleValue();
       } else {
-        return number.longValue();
+        return number.longValueExact();
       }

Replace number.longValue() with number.longValueExact() to prevent silent
integer overflow. Catch the resulting ArithmeticException for out-of-range
numbers and return a double as a fallback.

java/src/org/openqa/selenium/json/JsonInput.java [242-251]

 try {
   BigDecimal number = new BigDecimal(builder.toString());
   if (fractionalPart || number.stripTrailingZeros().scale() > 0) {
     return number.doubleValue();
   } else {
-    return number.longValue();
+    try {
+      return number.longValueExact();
+    } catch (ArithmeticException e) {
+      // It's too big for a long, so treat it as a double.
+      return number.doubleValue();
+    }
   }
 } catch (NumberFormatException e) {
   throw new JsonException("Unable to parse to a number: " + builder + ". " + input);
 }

[Suggestion processed]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a potential silent integer overflow when converting a BigDecimal to a long using longValue(). The proposed fix of using longValueExact() and falling back to a double on ArithmeticException is a robust solution that prevents data corruption for numbers exceeding the long range, which is a critical correctness improvement.

Medium
Learned
best practice
Deduplicate repeated numeric assertions

Replace the repeated toType assertions with a small helper (or a parameterized
test) that takes the JSON literal and expected numeric values, keeping the test
concise and easier to extend.

java/test/org/openqa/selenium/json/JsonTest.java [67-76]

-assertThat((Double) new Json().toType("4.2e+1", Double.class)).isEqualTo(42.0);
-assertThat((Integer) new Json().toType("4.2e+1", Integer.class)).isEqualTo(42);
-assertThat((Long) new Json().toType("4.2e+1", Long.class)).isEqualTo(42L);
-
-assertThat((Double) new Json().toType("42e+1", Double.class)).isEqualTo(420.0);
-assertThat((Integer) new Json().toType("42e+1", Integer.class)).isEqualTo(420);
-assertThat((Long) new Json().toType("42e+1", Long.class)).isEqualTo(420L);
-
+assertNumericConversions("4.2e+1", 42.0, 42, 42L);
+assertNumericConversions("42e+1", 420.0, 420, 420L);
 assertThat((Double) new Json().toType("42e-1", Double.class)).isEqualTo(4.2);
 assertThat((Double) new Json().toType("4.2e-1", Double.class)).isEqualTo(0.42);
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Reduce duplication by centralizing shared test behavior (helpers/parameterized tests) instead of repeating near-identical assertions.

Low
  • Update

@joerg1985
Copy link
Member

@asolntsev i think we are overengineering here. I would suggest to continue with the original PR and focus to the root issue.

@asolntsev
Copy link
Contributor Author

@asolntsev i think we are overengineering here. I would suggest to continue with the original PR and focus to the root issue.

That's my point: I tried to create a simpler solution :)

The case it solves is the following:

  • "42e-1" -> double
  • "42e+1" -> long

@joerg1985
Copy link
Member

joerg1985 commented Jan 23, 2026

The type guess issue does also happen in other areas. So i will create a second PR later to pull all guesses to one point. After this we can fix the issue, now it is just at one point so we have a different behavior inside the Json processing.

@joerg1985
Copy link
Member

I think we should really check each single input character:

    try (JsonInput input = newInput("0.٤2")) {
      assertThat(input.peek()).isEqualTo(NUMBER);
      assertThat(input.nextNumber()).isEqualTo(0.42);
    }

@asolntsev
Copy link
Contributor Author

overseeded by #16961

@asolntsev asolntsev closed this Jan 24, 2026
@asolntsev asolntsev deleted the fix/parsing-json-numbers-with-exponent branch January 24, 2026 10:13
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