checked_json 0.2.0
checked_json: ^0.2.0 copied to clipboard
A JSON object type you only get by checking a value, with typed readers that name the field that is absent or the wrong shape.
checked_json #
A JSON object you only get by checking a value. Holding a JsonObject means
the value was looked at and found to be a JSON object, so nothing downstream
checks it again. Its fields are read through typed readers that say which
field was wrong and what it should have been.
import 'package:checked_json/checked_json.dart';
final user = JsonObject.decode('{"name": "Ada", "tags": ["maths"]}');
user.string('name'); // 'Ada'
user.strings('tags'); // ['maths']
user.integerOrNull('born'); // null: absent
user.integer('name'); // throws JsonFieldException: `name` is not an integer
The doors in #
The map inside a JsonObject is private, so code outside the library cannot
wrap an unchecked value in one. A value becomes one through a door that checks
it:
JsonObject.decode(text)for wire text. AFormatExceptionwhen the text is not JSON, or is JSON of another kind.JsonObject.parse(value)for a value that is already decoded. Null when it is not a JSON object.object(k)andobjects(k)for an object nested in another.JsonObject.of(map)for an object you build yourself, for atoJson, from a map whose static type is already string-keyed.
The readers #
string, integer, number, boolean, object, list, strings and
objects. A plain reader requires its field and throws a
JsonFieldException naming the field and the shape it wanted. Its OrNull
twin takes an absent or null field as null, and still refuses one of the wrong
type. A default for an absent field is the caller's ??, so the default sits
where it is used. has(k) tells a field present with a null value apart from
an absent one, and raw(k) gives a field as it was decoded.
Building one object from others #
Each of these gives a new JsonObject, so no map has to come out to be
spread:
final streaming = body.withFields({'stream': true}); // fields laid over
final request = JsonObject.merge([common, if (effort != null) effort]);
// later parts win
final plain = streaming.without({'stream'}); // fields dropped
final kept = plain.frozen(); // read-only copy
For an object used as a dictionary, whose keys are data rather than names the
code knows, keys gives the names and a reader reads each value:
final prices = {for (final m in table.keys) m: table.number(m)};
jsonEncode writes a JsonObject as it is, so encoding needs no map. The one
way out to a Map is asUncheckedMap(), a read-only view for a parameter
declared as a Map, as some other package's API is. A read from it is
unchecked, which is what the name says.
JsonFieldException extends FormatException: a bad field is a condition
you're meant to catch, not a programming bug. Code that already catches
FormatException still catches it.
What it guarantees, and what it doesn't yet #
as JsonObjectandis JsonObjectdo not check. An extension type is erased at runtime, sox as JsonObjectisx as Map<String, Object?>and fails with aTypeErrorwhere a door would have thrown aFormatException. UseJsonObject.parseinstead, and the same goes for aJsonObjectpattern.- A
JsonObjectis not aMap.obj['k']does not compile, and neither doesobj['k'] = v, so a field is read through a reader orraw, and nothing outside the library writes to one. (Up to 0.1.x it implementedMap<String, Object?>.) It does implementObject, so it is never null and an extension onT extends Objectapplies to it. parsematches the map's runtime type.jsonDecodealways makes a string-keyed map, so its output is fine. AMap<dynamic, dynamic>, as YAML and Flutter platform channels deliver one, gives null even when every key is a string: copy it withMap<String, Object?>.fromfirst.- Whole numbers are exact only below 2^53 in magnitude.
integerreads3or3.0(as some writers spell one, and as a web build reads every whole number) but never3.5. A double is taken only when its size is strictly below 2^53, where it stands for one whole number; 2^53 itself is refused, because9007199254740993.0decodes to it too. - Nested values are not deep-checked up front. A door checks that the value is an object; each field is checked when its reader reads it.
Licence #
Apache-2.0. Copyright 2026 Enspyr Pty Ltd.