How to resolve deep json object in Dart

Issue #198

If we are not on the edge with GRPC and Protocol Buffer, then most likely we are going to deal with Restful and JSON. In one of my Flutter apps I needed to consume JSON

JSON and serialization

The guide at https://flutter.dev/docs/development/data-and-backend/json is definitely the way to go.

Currently there are 2 ways. One is to manually use dart:convert package

1
Map<String, dynamic> user = jsonDecode(jsonString);

The other way is to use json_serializable to generate parsing code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import 'package:json_annotation/json_annotation.dart';

/// This allows the `User` class to access private members in
/// the generated file. The value for this is *.g.dart, where
/// the star denotes the source file name.
part 'user.g.dart';

/// An annotation for the code generator to know that this class needs the
/// JSON serialization logic to be generated.
@JsonSerializable()

class User {
User(this.name, this.email);

String name;
String email;

/// A necessary factory constructor for creating a new User instance
/// from a map. Pass the map to the generated `_$UserFromJson()` constructor.
/// The constructor is named after the source class, in this case User.
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

/// `toJson` is the convention for a class to declare support for serialization
/// to JSON. The implementation simply calls the private, generated
/// helper method `_$UserToJson`.
Map<String, dynamic> toJson() => _$UserToJson(this);
}

json_resolve

The problem with manual approach is that it involves lot of boilerplate code, especially when accessing property inside deeply nested json. The problem with code generation approach is that it does not always fit our need and may lack of customization.

Therefore I created json_resolve which allows us to access json using keypath, with type checking and safety in mind. The code is small, simple to reason and tested.

1
2
3
4
5
6
7
8
9
10
11
final String byProperty = resolve(json: json, path: "movie", defaultValue: "error");
expect(byProperty, "isFun");

final int byInt = resolve(json: json, path: "earth", defaultValue: 0);
expect(byInt, 199999);

final String byIndex = resolve(json: json, path: "dc.2.name", defaultValue: "error");
expect(byIndex, "Wonder Woman");

final String byIndexThenProperty = resolve(json: json, path: "marvel.0.appear.1.title", defaultValue: "error");
expect(byIndexThenProperty, "The Dark World");

Comments