deep_pick 1.3.0
deep_pick: ^1.3.0 copied to clipboard
Simplifies manual JSON parsing with a type-safe API. No dynamic, no manual casting. Flexible inputs types, fixed output types. Useful parsing error messages
Changelog #
1.3.0 (13.10.26) #
-
New
mapValueforasMap*()andasJsonObject*()parses the values of a map, like the mapper ofasList*does for the items of a list #73. An error inside a value reports the full location, likeadidas.id.// {"nike": {"id": "421"}, "adidas": {"id": "532"}} Map<String, Shoe> shoes = pick(json).asJsonObjectOrEmpty(mapValue: Shoe.fromPick); -
New
whenNullValuemaps the entries with anullvalue, whichmapValueignores, likewhenNulldoes for lists.Map<String, Shoe?> shoes = pick(json).asJsonObjectOrEmpty( mapValue: Shoe.fromPick, whenNullValue: (pick) => null, ); -
New
mapKeyconverts the keys. It receives a pick of the key, so the usual parsers work on it.// {"421": {"name": "Nike Zoom Fly 3"}} Map<int, Shoe> shoes = pick(json).asMapOrEmpty( mapKey: (key) => key.asIntOrThrow(), mapValue: Shoe.fromPick, );The keys of
asJsonObject*()stayStrings, itsmapKeycan rename them. UseasMap*()to convert the keys to another type. -
All six parsers are affected:
asMapOrThrow(),asMapOrEmpty(),asMapOrNull(),asJsonObjectOrThrow(),asJsonObjectOrEmpty()andasJsonObjectOrNull().asJsonObject*()got two type parameters for it, a call without arguments still returns aMap<String, Object?>. -
New
asPlainListOrThrow(),asPlainListOrEmpty()andasPlainListOrNull()return the items of aListas they are, without a mapper #74. Declare the item type to get a typed list, an item of another type throws aPickExceptionthat names it.List<Object?> items = pick(json, 'tags').asPlainListOrEmpty(); List<String> tags = pick(json, 'tags').asPlainListOrEmpty();
1.2.0 (13.10.26) #
-
Better error messages #64, especially for absent values
PickException: expected a non-null value at shoes[0].name, but it is absent query shoes[0].name ~~~~ no such key at shoes[0] = {"id": 42, "size": "M"} hint Use asStringOrNull() when the value may be null/absent at some point (String?).A value that can't be parsed is shown with its type, and large values are shortened.
PickException: could not parse an int at shoes[0].size query shoes[0].size found "M" (a String) -
New
asJsonObjectOrThrow(),asJsonObjectOrEmpty()andasJsonObjectOrNull()return aMap<String, Object?>that goes straight into afromJsonfunction, no type arguments needed #70. Entries with a key that isn't aStringare ignored. -
New
asJsonObjectListOrThrow(),asJsonObjectListOrEmpty()andasJsonObjectListOrNull()for a list of JSON objects #70.// before pick(json, 'users').asListOrEmpty((pick) => pick.asMapOrThrow<String, dynamic>()); // after pick(json, 'users').asJsonObjectListOrEmpty(); -
New
asMapListOrThrow<K, V>(),asMapListOrEmpty<K, V>()andasMapListOrNull<K, V>()for a list of maps with any key type #70. -
Fix
Pick.isAbsentistruebelow anull, however the path is written #64.pick({'a': null}, 'a', 'b').isAbsentwasfalse, whilepick({'a': null}, 'a')('b').isAbsentwastrue. -
New
PickException.fromPick(pick, 'expected a Timestamp')lets custom parsers throw errors like the built-in ones #64. -
New
Pick.redactValues()masks error values while keeping keys and types visible;Pick.redactsValuesexposes the policy to custom parsers andRequiredPick.redactValues()preserves non-null chaining #65. -
Fix
as*OrThrow()andletOrThrow()no longer leave an internal_required_pick_error_hintentry inPick.context. That also makes them as fast as theirOrNullcounterparts #68. -
Deprecated
Pick.path,Pick.missingValueAtIndex,Pick.followablePathandPick.debugParsingExit. The error message of aPickExceptionshows where a pick failed. A custom parser that built its message from them throwsPickException.fromPick(this, 'expected a Foo')instead #69.
1.1.0 (30.08.24) #
- Allow
.letOrNull((pick) => null)to returnnullwithout manually setting a nullable type #61 - CI 2024 update
1.0.0 (19.03.23) #
0.10.0 (01.10.21) #
- New: Support for more date formats.
asDateTime*received an optionalformatparameter. By default, all possible formats will be parsed. To the existingISO 8601format,RFC 1123,RFC 850andasctimehave been added which are typically used for the HTTP header or cookies. - Documentation added for
asDoubleandasMap
0.9.0 (02.08.21) #
-
New:
pickFromJson(json, args...)allows parsing of a json String, without manually callingjsonDecode#41 -
New:
pickDeep(json, 'some.key.inside.the.object'.split('.'))allows picking with a dynamic depth #40 -
Add
Pick.indexto get the element index for list items #38pick(["John", "Paul", "George", "Ringo"]).asListOrThrow((pick) { final index = pick.index!; return Artist(id: index, name: pick.asStringOrThrow()); ); -
Pick.asIntOrThrow()now allows parsing of doubles when one of the newroundDoubleortruncateDoubleparameters istrue#37. Thx @stevendz -
Add dartdoc to
asList*()extensions
0.8.0 (and 0.6.10 for Dart <2.12) (12.02.21) #
- Deprecated parsing extensions of
RequiredPickto acknowledge that all parsers eventually causes errors. From now on, always use.asIntOrThrow()instead of.required().asInt(). Only exception is.required().toString(). Read more in #34 - Replace
dynamicwithObjectwhere possible - Rename
Pick.location()toPick.debugParsingExit - Removal of
PickLocaitonandPickContextmixins. They are now part ofPick RequiredPicknow extendsPickmaking it easier to write parsers for custom types
0.7.0 #
- Enable nullsafety (requires Dart >=2.12)
0.6.10 #
Backports 0.8.0 to pre-nullsafety
0.6.0 #
API changes #
-
Remove long deprecated
parseJsonTo*methods. Use thepick(json, args*)api -
New
asXyzOrThrow()methods as shorthand for.required().asXyz()featuring better error messagesasBoolOrThrow()asDateTimeOrThrow()asDoubleOrThrow()asIntOrThrow()letOrThrow()asListOrThrow()asMapOrThrow()asStringOrThrow()
-
New
Pick.isAbsentgetter to check if a value is absent ornull#24. Absent could mean- Accessing a key which doesn't exist in a
Map - Reading the value from
Listwhen the index is greater than the length - Trying to access a key in a
Mapbut the found data aObjectwhich isn't a Map
- Accessing a key which doesn't exist in a
-
New
RequiredPick.nullable()converting aRequiredPickback to aPickwith potentialnullvalue -
New
PickLocation.followablePath. WhilePickLocation.pathcontains the full path to the value,followablePathcontains only the part which could be followed with a non-nullable value -
Breaking
asList*()now requires the mapping function. -
Breaking
asList*()now ignoresnullvalues when parsing. The map function now receives aRequiredPickas fist parameter instead of aPickwith a potential null value, making parsing easier.Therefore
pick().required().asList((RequiredPick pick) => /*...*/)only maps non-nullable values. When your lists containnullit will be ignored. This is fine in most cases and simplifies the map function.In rare cases, where your lists contain
nullvalues with meaning, use the second parameterwhenNullto map those null values.asList((pick) => Person.fromPick(pick), whenNull: (Pick it) => null). The function still receives aPickwhich gives access to thecontextapi or thePickLocation. But thePicknever holds any value.
Parsing changes #
0.6.0-nullsafety.2 #
-
Breaking
asList*()methods now ignorenullvalues. The map function now receives aRequiredPickas fist parameter instead of aPickmaking parsing easier.Therefore
pick().required().asList((RequiredPick pick) => /*...*/)only maps non-nullable values. When your lists containnullit will be ignored. This is fine in most cases and simplifies the map function.In rare cases, where your lists contain
nullvalues with meaning, use the second parameterwhenNullto map those null values.asList((pick) => Person.fromPick(pick), whenNull: (Pick it) => null). The function still receives aPickwhich gives access to thecontextapi or thePickLocation. But thePicknever holds any value. -
Breaking Don't parse doubles as int because the is no rounding method which satisfies all #31
-
Breaking Allow parsing of "german" doubles with
,as decimal separator #30 -
Improve error messages with more details where parsing stopped
-
New
RequiredPick.nullable()converting aRequiredPickback to aPickwith potentialnullvalue -
New
PickLocation.followablePath. WhilePickLocation.pathcontains the full path to the value,followablePathcontains only the part which could be followed with a non-nullable value
0.6.0-nullsafety.1 #
- New
asXyzOrThrow()methods as shorthand for.required().asXyz()featuring better error messagesasBoolOrThrow()asDateTimeOrThrow()asDoubleOrThrow()asIntOrThrow()letOrThrow()asListOrThrow()asMapOrThrow()asStringOrThrow()
- New
Pick.isAbsentgetter to check if a value is absent ornull#24. Absent could mean- Accessing a key which doesn't exist in a
Map - Reading the value from
Listwhen the index is greater than the length - Trying to access a key in a
Mapbut the found data aObjectwhich isn't a Map
- Accessing a key which doesn't exist in a
- The String
"true"and"false"are now parsed as boolean - More nnbd refactoring
0.6.0-nullsafety.0 #
- Migrate to nullsafety (required Dart >=2.12)
- Remove long deprecated
parseJsonTo*methods. Use thepick(json, args*)api - Improve dartdoc
0.5.1 #
- Rename
Pick.addContexttoPick.withContextusing deprecation Pick.fromContextnow accepts 10 arguments for nested structures- Fix
Pick.fromContextalways returningcontextnot the value forkeyin contextMap
0.5.0 #
-
New context API. You can now attach relevant additional information for parsing directly to the
Pickobject. This allows passing information intofromPickconstructors without adding new parameters to all constructors in between.// Add context final shoes = pick(json, 'shoes') .addContext('apiVersion', "2.3.0") .addContext('lang', "en-US") .asListOrEmpty((p) => Shoe.fromPick(p.required()));import 'package:version/version.dart'; // Read context factory Shoe.fromPick(RequiredPick pick) { // read context API final version = pick.fromContext('newApi').required().let((pick) => Version(pick.asString())); return Shoe( id: pick('id').required().asString(), name: pick('name').required().asString(), // manufacturer is a required field in the new API manufacturer: version >= Version(2, 3, 0) ? pick('manufacturer').required().asString() : pick('manufacturer').asStringOrNull(), tags: pick('tags').asListOrEmpty(), ); } -
Breaking:
PickandRequiredPickhave chained their constructor signature.pathis now a named argument andcontexthas been added.
- RequiredPick(this.value, [this.path = const []])
+ RequiredPick(this.value, {this.path = const [], Map<String, dynamic> context})
- The
pathis now correctly forwarded afterPick#callorPick#asListOrEmptyand always shows the full path since origin
0.4.3 #
- Fix error reporting for
asMapOr[Empty|Null]and don't swallow parsing errors - Throw Map cast errors when parsing, not lazily when accessing the data
0.4.2 #
- Fix error reporting of
asListOrNull(mapToUser)andasListOrEmpty(mapToUser). Both now return errors during mapping and don't swallow them
0.4.1 #
- Print correct path in error message when json is
null asDateTime()now skips parsing when the value is already aDateTime
0.4.0 #
Map objects #
New APIs to map picks to objects and to map list elements to objects.
RequiredPick.let<R>(R Function(RequiredPick pick) block): R
Pick.letOrNull<R>(R Function(RequiredPick pick) block): R
RequiredPick.asList<T>([T Function(Pick) map]): List<T>
Pick.asListOrNull<T>([T Function(Pick) map]): List<T>
Pick.asListOrEmpty<T>([T Function(Pick) map]): List<T>
Here are two example how to actually use them.
// easily pick and map objects to dart objects
final Shoe oneShoe = pick(json, 'shoes', 0).letOrNull((p) => Shoe.fromPick(p));
// map list of picks to dart objects
final List<Shoe> shoes =
pick(json, 'shoes').asListOrEmpty((p) => Shoe.fromPick(p.required()));
Required picks #
Pick now offers a new required() method returning a RequiredPick. It makes sure the picked value exists or crashes if it is null. Because it can't be null, RequiredPick doesn't offer fallback methods like .asIntOrNull() but only .asInt(). This makes the API a bit easier to use for values you can't live without.
// use required() to crash if a object doesn't exist
final name = pick(json, 'shoes', 0, 'name').required().asString();
print(name); // Nike Zoom Fly 3
Note: Calling .asString() directly on Pick has been deprecated. You now have to call required() first to convert the Pick to a RequiredPick or use a mapping method with fallbacks.
Pick deeper #
Ever got a Pick/RequiredPick and you wanted to pick even further. This is now possible with the call method. Very useful in constructors when parsing methods.
factory Shoe.fromPick(RequiredPick pick) {
return Shoe(
id: pick('id').required().asString(),
name: pick('name').required().asString(),
manufacturer: pick('manufacturer').asStringOrNull(),
tags: pick('tags').asListOrEmpty(),
);
}
Bugfixes #
- Don't crash when selecting a out of range index from a
List .asMap(),.asMapOrNull()and.asMapOrEmpty()now consistently returnMap<dynamic, dynamic>(wasMap<String, dynamic>)
Also the lib has been converted to use static extension methods which were introduced in Dart 2.6
0.3.0 #
asMap now expects the key type, defaults to dynamic instead of String
-Pick.asMap(): Map<String, dynamic>
+Pick.asMap<T>(): Map<T, dynamic>
0.2.0 #
New API!
The old parse* methods are now deprecated, but still work.
Replace them with the new pick(json, arg0-9...) method.
- final name = parseJsonToString(json, 'shoes', 0, 'name');
+ final name = pick(json, 'shoes', 0, 'name').asString();
pick returns a Pick which offers a rich API to parse values.
.asString()
.asStringOrNull()
.asMap()
.asMapOrEmpty()
.asMapOrNull()
.asList()
.asListOrEmpty()
.asListOrNull()
.asBool()
.asBoolOrNull()
.asBoolOrTrue()
.asBoolOrFalse()
.asInt()
.asIntOrNull()
.asDouble()
.asDoubleOrNull()
.asDateTime()
.asDateTimeOrNull()
0.1.1 #
- pubspec description updated
0.1.0 #
- Initial version