Skip to content

Add JsonNumericMode parameter to jsonDecode for opt-in all-double parsing#62777

Open
fabricio-costa wants to merge 2 commits intodart-lang:mainfrom
fabricio-costa:feature/json-numeric-mode
Open

Add JsonNumericMode parameter to jsonDecode for opt-in all-double parsing#62777
fabricio-costa wants to merge 2 commits intodart-lang:mainfrom
fabricio-costa:feature/json-numeric-mode

Conversation

@fabricio-costa
Copy link

Summary

  • Adds JsonNumericMode enum to dart:convert with two values: preserveType (default) and allDouble
  • Adds optional numericMode parameter to jsonDecode(), JsonCodec.decode(), and JsonDecoder
  • When allDouble is used, all JSON numbers are parsed as double, aligning with ECMA-404's single "number" type

This is 100% backwards compatible — the default behavior is unchanged.

Motivation

jsonDecode('{"price": 5}') returns int for price, but jsonDecode('{"price": 5.0}') returns double. Since JSON makes no distinction between the two, this causes TypeError in Flutter apps when a backend returns a number without a decimal point for a field expected as double.

The current workaround ((json['x'] as num).toDouble()) must be applied manually to every double field in every model class. This proposal provides an opt-in solution at the parser level.

Related issue: #62776
See also: #46883, #42622, #55499, #56913

Implementation

The conversion happens at the listener level, not in parseNumber:

Platform Change
VM _JsonListener.handleNumber converts intdouble when allDouble
JS Wraps JSON.parse post-processing with int→double synthetic reviver
WASM _JsonListener.handleIntegerNumber short-circuits to double

This keeps the performance-critical parsing code untouched. Zero overhead when using the default preserveType mode.

Usage

// Opt-in: all numbers become double
final data = jsonDecode(
  '{"price": 29, "quantity": 3}',
  numericMode: JsonNumericMode.allDouble,
);
print(data['price'] is double); // true

// Default: unchanged behavior
final data2 = jsonDecode('{"price": 29}');
print(data2['price'] is int); // true

Files changed

  • sdk/lib/convert/json.dart — public API (enum + parameters)
  • sdk/lib/_internal/vm/lib/convert_patch.dart — VM backend
  • sdk/lib/_internal/js_runtime/lib/convert_patch.dart — JS backend
  • sdk/lib/_internal/wasm/lib/convert_patch.dart — WASM backend
  • tests/lib/convert/json_numeric_mode_test.dart — test coverage

Test plan

  • Existing tests/lib/convert/json_* tests pass (backwards compat)
  • New json_numeric_mode_test.dart covers:
    • Default preserveType behavior unchanged
    • allDouble converts integers to double (basic, zero, negative, large)
    • Scientific notation handled correctly
    • Non-numeric values (null, bool, string) unaffected
    • Nested structures (objects, arrays, deeply nested)
    • Reviver integration (receives converted doubles)
    • JsonCodec.decode() and JsonDecoder constructor paths

@google-cla
Copy link

google-cla bot commented Feb 26, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

…sing

JSON (ECMA-404) does not distinguish between integer and floating-point
numbers — both are simply "number". Dart's jsonDecode currently returns
int for numbers without a decimal point and double otherwise, which
causes TypeError when consuming APIs that may return either form for
the same field.

This adds an optional numericMode parameter to jsonDecode, JsonCodec.decode,
and JsonDecoder that controls how numbers are parsed:

- JsonNumericMode.preserveType (default): current behavior, no changes
- JsonNumericMode.allDouble: all JSON numbers are parsed as double

The implementation converts int to double at the listener level in each
platform backend (VM, JS runtime, WASM), keeping the performance-critical
parseNumber logic untouched.

Closes dart-lang#62776
See also dart-lang#46883, dart-lang#55499
@fabricio-costa fabricio-costa force-pushed the feature/json-numeric-mode branch from ac6d163 to 912d513 Compare February 26, 2026 20:06
@copybara-service
Copy link

Thank you for your contribution! This project uses Gerrit for code reviews. Your pull request has automatically been converted into a code review at:

https://dart-review.googlesource.com/c/sdk/+/483941

Please wait for a developer to review your code review at the above link; you can speed up the review if you sign into Gerrit and manually add a reviewer that has recently worked on the relevant code. See CONTRIBUTING.md to learn how to upload changes to Gerrit directly.

Additional commits pushed to this PR will update both the PR and the corresponding Gerrit CL. After the review is complete on the CL, your reviewer will merge the CL (automatically closing this PR).

@copybara-service
Copy link

Thank you for your contribution! This project uses Gerrit for code reviews. Your pull request has automatically been converted into a code review at:

https://dart-review.googlesource.com/c/sdk/+/483940

Please wait for a developer to review your code review at the above link; you can speed up the review if you sign into Gerrit and manually add a reviewer that has recently worked on the relevant code. See CONTRIBUTING.md to learn how to upload changes to Gerrit directly.

Additional commits pushed to this PR will update both the PR and the corresponding Gerrit CL. After the review is complete on the CL, your reviewer will merge the CL (automatically closing this PR).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant