Repository navigation
v2.11.0 - fix: Json decode<Map> cast nested lists to List<Map>; dart_style 3.1.13, analyzer 14 - #56
Merged
Merged
Conversation
… (v2.10.2) The values of an untyped Map were decoded with the Map TypeInfo itself, so a nested JSON list took Map as its element type and was wrapped by List.cast<Map>(): decoding succeeded, and reading (or adding) a non-Map element threw later. An untyped List had the same defect for its elements. A collection TypeInfo without arguments now decodes its values/elements as dynamic, matching dart:convert's jsonDecode. Typed collections and entity types are unchanged. Adds a "JsonDecoder untyped collections" test group covering every decode entry point (sync, async, bytes, fromJson*, fromJsonMap*) over scalar, mixed, null, empty, nested and deep lists, compared against jsonDecode. Also updates dependencies within the current constraints (analyzer 14 is blocked by the dart_style 3.1.10 pin). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Dart 3.13.2 bundles dart_style 3.1.13 (3.13.0-3.13.1 bundle 3.1.10), so the 3.1.10 pin no longer matched `dart format` on current SDKs. Move the pin and raise the SDK floor with it, as in 2.9.0; the narrower SDK range makes this a minor bump (2.10.2 -> 2.11.0, unreleased). dart_style 3.1.13 accepts analyzer 14, so move to analyzer ^14.5.0, plus build_runner ^2.16.2 and build_test ^3.5.21. Generated code is unchanged apart from the version string. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #56 +/- ##
==========================================
+ Coverage 89.56% 92.56% +2.99%
==========================================
Files 14 14
Lines 6423 6428 +5
==========================================
+ Hits 5753 5950 +197
+ Misses 670 478 -192
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…N/reflection coverage Decode fix: - Untyped Set/Iterable, a non-standard decoder (no raw fast path), JsonCodec, encode/decode round trips, 200-level nesting, a 1000-element mixed list, unusual keys/strings. - Entity integration: new TestPayloadWithReflection with Map, Map<String, dynamic>, List<Map>, List<List>, List and dynamic fields, through every decode entry point, generated $fromJson/$fromJsonEncoded, round trips and lists of entities. Reflection records these fields with explicit dynamic arguments, so they were not affected by the bug; the tests guard them. General: - reflection_factory_json_paths_test.dart: async decoding with Future inputs/values, decoder/encoder hooks and fallbacks, scalar parsing, callToJson, isStandard, MimeTypeResolverExtension. - reflection_factory_paths_test.dart: MethodInvocation, optional positional parameters, JsonField accessors, EnumReflection, static field setters, constructor invocation errors. Line coverage: reflection_factory_json.dart 86.9% -> 95.9%, reflection_factory_base.dart 83.1% -> 89.5%. 516 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#56) The nested-list fix sent every collection TypeInfo to `dynamic`, including ones with arguments, so `Map<String, int>`, `Map<String, Entity>` and `List<Entity>` element/value types were dropped (entities stayed raw maps). Only a collection *without* arguments falls back to `dynamic` now, through one shared `_untypedCollectionAsDynamic` used by both decode sites. Adds regression tests (sync and async) for the three cases; they fail on the previous commit. CHANGELOG: document the behavior changes (List<dynamic> instead of List<Map>/List<List> views; type decoders keyed on Map/List no longer see nested scalars), the analyzer major-version note, and correct the generated code wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Values keep the iterable TypeInfo (only list values become entities). Using
its element type instead would turn map values into entities and scalars
into null (1) or entities built from a string ("x"). The suite passed with
that variant; this test fails on it and passes on master and on this branch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix: nested JSON lists decoded as lazily cast
List<Map>JsonDecoder.decode<Map>/decodeAsync<Map>(anddecodeFromBytes*,fromJson*,fromJsonMap*,JsonCodec.decode*) decoded the values of an untypedMapwith theMapTypeInfoitself. A nested JSON list therefore tookMapas its element type and was wrapped byList.cast<Map>(): decoding succeeded, then reading (or adding) a non-Mapelement threw aTypeErrorfar from thedecodecall. An untypedList/Set/Iterablehad the same defect for its elements.A collection type without arguments (
Map,List,Set,Iterable) now decodes its values/elements asdynamic, so the result matchesdart:convert'sjsonDecode. Collections with arguments (Map<String, int>,List<User>) and entity types are unchanged.Review follow-up: the first version of the fix also sent collections with arguments to
dynamic, droppingMap<String, int>/Map<String, Entity>/List<Entity>decoding. Fixed in 5235232 with one shared rule (_untypedCollectionAsDynamic), plus regression tests (sync and async) that fail on the previous commit.Behavior changes (documented in the CHANGELOG): nested lists under untyped collections are now
List<dynamic>rather thanList<Map>/List<List>views; type decoders keyed onMap/Listno longer see the scalars nested under an untyped collection.Tests
For the fix (
JsonDecoder untyped collectionsgroups): every decode entry point compared againstjsonDecode— scalars, nulls, mixed types, map-first vs scalar-first, nested/empty lists, date-like strings, top-level lists, untypedSet/Iterable, a non-standard decoder (no raw fast path),JsonCodec, encode→decode round trips, 200-level nesting, a 1000-element mixed list, unusual keys, writes to decoded lists. The fix-specific tests fail on the old code.Entity integration: new
TestPayloadWithReflection(Map,Map<String, dynamic>,List<Map>,List<List>,List,dynamicfields) through every decode entry point, generated$fromJson/$fromJsonEncoded, round trips and lists of entities. Reflection records these fields with explicitdynamicarguments, so they were not affected by the bug; these tests guard that path.General coverage:
reflection_factory_json_paths_test.dart: async decoding withFutureinputs/values, decoder/encoder hooks and their fallbacks, scalar parsing,callToJson,isStandard,MimeTypeResolverExtension.reflection_factory_paths_test.dart:MethodInvocation, optional positional parameters,JsonFieldaccessors,EnumReflection, static field setters, constructor invocation errors.Line coverage:
reflection_factory_json.dart86.9% → 95.9%,reflection_factory_base.dart83.1% → 89.5%.Dependencies
dart_style:>=3.1.10 <3.1.11→>=3.1.13 <3.1.14. Dart 3.13.2 bundles 3.1.13 (3.13.0–3.13.1 bundle 3.1.10), so the old pin no longer matcheddart format.environment.sdk:>=3.13.0→>=3.13.2, keeping the pin matched to the consumer's SDK (as in 2.9.0). Narrower SDK range → minor bump.analyzer:^13.0.0→^14.5.0(allowed bydart_style3.1.13); no code changes needed.build,mime,pub_semver,yaml,build_runner,build_test,pubspec_parse,data_serializer,test.Regenerated code is unchanged apart from the version string (and the new test entity).
Verification
dart analyze: no issuesdart format --set-exit-if-changed lib test example: cleandart test: 520 passed (VM); changed test files also pass on Chrome🤖 Generated with Claude Code