/dart-json-parser

Runtime type-safety for deserialising JSON values in Dart

Primary LanguageDartMozilla Public License 2.0MPL-2.0

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

Features

Safely convert a dynamic-typed value returned from dart:convert / JsonDecoder / #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:

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):

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

Usage

The example in example/ is executable. Copied here:

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<StringAndInt> 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<JsonParseException>(FormatException: Expecting object, got Null: null)
wrong top-level type: Left<JsonParseException>(FormatException: Expecting object, got int: 3)
missing key: Left<JsonParseException>(FormatException: Expected to find key str-foobar in object: {irrelevant: 5})
wrong type at value: Left<JsonParseException>(FormatException: Expected String, got int: 100)
ok: Right<StringAndInt>(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 library for JSON parsing and formatting. Secondarily, the existance of the great project 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.