Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/easoncxz/dart-json-parser

Runtime type-safety for deserialising JSON values in Dart
https://github.com/easoncxz/dart-json-parser

Last synced: 13 days ago
JSON representation

Runtime type-safety for deserialising JSON values in Dart

Awesome Lists containing this project

README

        

Compile-time-checked runtime-type-safety during JSON deserialisation

## Features

Safely convert a `dynamic`-typed value returned from `dart:convert` /
[`JsonDecoder`](https://api.dart.dev/stable/2.16.2/dart-convert/JsonDecoder-class.html)
/ `#convert` into a value of a type in your application domain. If your parser
compiles, there should be no runtime type-errors thrown during
deserialisation.

You need to write parsers for your domain type using this library. See example
below for what you need to do.

## Getting started

You can use this library straight from Github here. In your `pubspec.yaml`:

```yaml
dependencies:
json_parser:
git: https://github.com/easoncxz/dart-json-parser
```

Then, anywhere in your code (but probably close to your domain models or
serialisation logic):

```dart
import 'package:json_parser/json_parser.dart' as P;
```

## Usage

The example in `example/` is executable. Copied here:

```dart
import 'dart:convert' as C;
import 'package:json_parser/json_parser.dart' as P;

// Part of your application domain
class StringAndInt {
final String s;
final int i;
StringAndInt(this.s, this.i);
String toString() =>
'StringAndInt(${this.s}, ${this.i})';
}

// Definitive source of how to deserialise
P.Parser parseStringAndInt(dynamic v) =>
P.withObject(v, (o) =>
P.Parser.pure(StringAndInt(
P.parseField(o, 'str-foobar', P.parseString).force(),
P.parseField(o, 'int67', P.parseInt).force(),
))
);

void main() {
final decoder = C.JsonDecoder();
print('is null: ${parseStringAndInt(decoder.convert('null')).safe()}');
print('wrong top-level type: ${parseStringAndInt(decoder.convert('3')).safe()}');
print('missing key: ${parseStringAndInt(decoder.convert('{ "irrelevant": 5 }')).safe()}');
print('wrong type at value: ${parseStringAndInt(decoder.convert('{ "str-foobar": 100 }')).safe()}');
print('ok: ${parseStringAndInt(decoder.convert('{ "str-foobar": "something", "int67": 1 }')).safe()}');
}
```

To run this example yourself:

```
$ dart run example/json_parser_example.dart
```

Expected standard output:

```
is null: Left(FormatException: Expecting object, got Null: null)
wrong top-level type: Left(FormatException: Expecting object, got int: 3)
missing key: Left(FormatException: Expected to find key str-foobar in object: {irrelevant: 5})
wrong type at value: Left(FormatException: Expected String, got int: 100)
ok: Right(StringAndInt(something, 1))
```

## Additional information

### Motivation

I implemented this library mostly for fun, but also because I was looking for
an equivalent library to this one, but I couldn't find it.

### Inspiration

First and foremost, the idea of these combinators come from the Haskell
ecosystem, in particular from the [Aeson][aeson] library for JSON parsing and
formatting. Secondarily, the existance of the great project [`io-ts`][io-ts] in
the TypeScript ecosystem gives me confidence that such a type-safe parser could
probably be written in even some languages that lean towards object-oriented
programming a bit more.

## License

Mozilla Public License Version 2.0. See `LICENSE`.

[aeson]: https://hackage.haskell.org/package/aeson
[io-ts]: https://github.com/gcanti/io-ts