json_types

License Pub.dev Github Stars

Type-safe JSON serialization/deserialization

NO CODE GENERATION OR REFLECTION

Example:

Simple types:

/// Example of a class that extends [Json] and uses [JsonKey]s to define
/// properties.
final class TestObject extends Json {
  final stringJson = Json.string('myStringKey');
  final doubleJson = Json.double('myDoubleKey');
  final intJson = Json.int('myIntKey');
  final boolJson = Json.boolean('myBoolKey');

  String get myString => stringJson.value;
  double get myDouble => doubleJson.value;
  int get myInt => intJson.value;
  bool get myBool => boolJson.value;

  TestObject.parser() : super();

  TestObject.parse(super.json) : super.parse();

  TestObject.populated({
    required String str,
    required double d,
    required int i,
    required bool b,
  }) : super() {
    stringJson.populate(str);
    doubleJson.populate(d);
    intJson.populate(i);
    boolJson.populate(b);
  }

  @override
  List<JsonKey<dynamic, dynamic>> get keys =>
      [stringJson, doubleJson, intJson, boolJson];
}

/// Create a [TestObject] from in-memory data.
final object1 = TestObject.populated(str: 'testStr', d: 12.5, i: 10, b: false);

/// A JSON representation of [object1].
final objectJson1 = {
  'myStringKey': 'testStr',
  'myDoubleKey': 12.5,
  'myIntKey': 10,
  'myBoolKey': false,
};

/// Parse the JSON representation of [object1] into a [TestObject].
TestObject.parse(objectJson1);

/// Convert [object1] to JSON.
object1.toJson();

/// Create a [TestObject] from in-memory data.
final object2 =
    TestObject.populated(str: 'testStr2', d: 102.5, i: -5, b: true);

/// A JSON representation of [object2].
final objectJson2 = {
  'string': 'testStr2',
  'double': 102.5,
  'int': -5,
  'bool': true,
};

TestObject.parse(objectJson2); /// Deserialize JSON into a [TestObject].
object2.toJson(); /// Serialize a [TestObject] into JSON.

Optional types:

/// Example of a class that extends [Json] and uses [JsonKey]s to define
/// optional properties.
final class TestOptionalObject extends Json {
  final stringJson = Json.optionalString('myStringKey');
  final doubleJson = Json.optionalDouble('myDoubleKey');
  final intJson = Json.optionalInt('myIntKey');
  final boolJson = Json.optionalBoolean('myBoolKey');

  String? get myString => stringJson.value;
  double? get myDouble => doubleJson.value;
  int? get myInt => intJson.value;
  bool? get myBool => boolJson.value;

  TestObject.parser() : super();

  TestObject.parse(super.json) : super.parse();

  TestObject.populated({
    String? str,
    double? d,
    int? i,
    bool? b,
  }) : super() {
    stringJson.populate(str);
    doubleJson.populate(d);
    intJson.populate(i);
    boolJson.populate(b);
  }

  @override
  List<JsonKey<dynamic, dynamic>> get keys =>
      [stringJson, doubleJson, intJson, boolJson];
}

/// Create a [TestOptionalObject] from in-memory data.
final object1 = TestObject.populated(str: 'testStr', d: 12.5, i: 10, b: false);

/// A JSON representation of [object1].
final objectJson1 = {
  'myStringKey': 'testStr',
  'myDoubleKey': 12.5,
  'myIntKey': 10,
  'myBoolKey': false,
};

/// Parse the JSON representation of [object1] into a [TestObject].
TestObject.parse(objectJson1);

/// Convert [object1] to JSON.
object1.toJson();

/// Create a [TestOptionalObject] from in-memory data.
final object2 =
    TestObject.populated(str: 'testStr2', b: true);

/// A JSON representation of [object2].
final objectJson2 = {
  'string': 'testStr2',
  'bool': true,
};

TestObject.parse(objectJson2); /// Deserialize JSON into a [TestObject].
object2.toJson(); /// Serialize a [TestObject] into JSON.

Inheritance/Polymorphic types:

/// Example of a class that extends [Json] and uses [JsonKey]s to define
/// objects with inheritance.
sealed class TestPolymorphic extends JsonPolymorphic<TestPolymorphic> {
  final stringJson = Json.string('string');
  final doubleJson = Json.double('double');

  String get myString => stringJson.value;
  double get myDouble => doubleJson.value;

  TestPolymorphic() : super();

  TestPolymorphic.parse(super.json) : super.parse();

  factory TestPolymorphic.polymorphicParse(Map<String, dynamic> json) =>
      JsonPolymorphic.polymorphicParse(json, [
        TestPolymorphicA.parser,
        TestPolymorphicB.parser,
      ]);

  TestPolymorphic.populated({
    required String str,
    required double d,
  }) : super() {
    stringJson.populate(str);
    doubleJson.populate(d);
  }

  @override
  List<JsonKey<dynamic, dynamic>> get keys => [stringJson, doubleJson];
}

/// Example of a class that extends [TestPolymorphic] and uses [JsonKey]s to
/// define objects with inheritance.
final class TestPolymorphicA extends TestPolymorphic {
  @override
  String get type => 'TestPolymorphicA';

  final intJson = Json.int('int');
  final boolJson = Json.boolean('bool');

  int get myInt => intJson.value;
  bool get myBool => boolJson.value;

  TestPolymorphicA.parser() : super();

  TestPolymorphicA.parse(super.json) : super.parse();

  TestPolymorphicA.populated({
    required super.str,
    required super.d,
    required int i,
    required bool b,
  }) : super.populated() {
    intJson.populate(i);
    boolJson.populate(b);
  }

  @override
  List<JsonKey<dynamic, dynamic>> get keys =>
      [...super.keys, intJson, boolJson];
}

/// Another example of a class that extends [TestPolymorphic] and uses [JsonKey]s to
/// define objects with inheritance.
final class TestPolymorphicB extends TestPolymorphic {
  @override
  String get type => 'TestPolymorphicB';
  
  final stringListJson = Json.stringList('stringList');
  final doubleListJson = Json.doubleList('doubleList');

  List<String> get myStringList => stringListJson.value;
  List<double> get myDoubleList => doubleListJson.value;

  TestPolymorphicB.parser() : super();

  TestPolymorphicB.parse(super.json) : super.parse();

  TestPolymorphicB.populated({
    required super.str,
    required super.d,
    required List<String> stringList,
    required List<double> doubleList,
  }) : super.populated() {
    stringListJson.populate(stringList);
    doubleListJson.populate(doubleList);
  }

  @override
  List<JsonKey<dynamic, dynamic>> get keys =>
      [...super.keys, stringListJson, doubleListJson];
}

/// Create a [TestPolymorphicA] from in-memory data.
final polymorphicA = TestPolymorphicA.populated(
  str: 'testStr',
  d: 12.5,
  i: 10,
  b: false,
);

/// A JSON representation of [polymorphicA].
final polymorphicJsonA = {
  '__poly__': 'TestPolymorphicA',
  'string': 'testStr',
  'double': 12.5,
  'int': 10,
  'bool': false,
};

final polymorphicB = TestPolymorphicB.populated(
  str: 'testStr',
  d: 12.5,
  stringList: ['string1', 'string2'],
  doubleList: [2.5, 3.5],
);

/// A JSON representation of [polymorphicB].
final polymorphicJsonB = {
  '__poly__': 'TestPolymorphicB',
  'string': 'testStr',
  'double': 12.5,
  'stringList': ['string1', 'string2'],
  'doubleList': [2.5, 3.5],
};

/// Deserializes JSON into a [TestPolymorphicA].
TestPolymorphic.polymorphicParse(polymorphicJsonA);

/// Deserializes JSON into a [TestPolymorphicB].
TestPolymorphic.polymorphicParse(polymorphicJsonB);

Maps:

/// Example of a class that extends [Json] and uses [JsonKey]s to define
/// maps.
final class TestMaps extends Json {
  final stringMap = Json.stringMap('myStringMapKey');
  final doubleMap = Json.doubleMap('myDoubleMapKey');
  final intMap = Json.intMap('myIntMapKey');
  final booleanMap = Json.booleanMap('myBooleanMapKey');
  final objectMap = Json.objectMap('myObjectMapKey', TestObject.parser);
  final polymorphicMap = 
    Json.polymorphicMap('myPolymorphicMapKey', TestPolymorphic.polymorphicParse);

  Map<String, String> get myStringMap => stringMap.value;
  Map<String, double> get myDoubleMap => doubleMap.value;
  Map<String, int> get myIntMap => intMap.value;
  Map<String, bool> get myBooleanMap => booleanMap.value;
  Map<String, TestObject> get myObjectMap => objectMap.value;
  Map<String, TestPolymorphic> get myPolymorphicMap => polymorphicMap.value;

  TestMaps.parser() : super();

  TestMaps.parse(super.json) : super.parse();

  TestMaps.populated({
    required Map<String, String> stringMap,
    required Map<String, double> doubleMap,
    required Map<String, int> intMap,
    required Map<String, bool> booleanMap,
    required Map<String, TestObject> objectMap,
    required Map<String, TestPolymorphic> polymorphicMap,
  }) : super() {
    this.stringMap.populate(stringMap);
    this.doubleMap.populate(doubleMap);
    this.intMap.populate(intMap);
    this.booleanMap.populate(booleanMap);
    this.objectMap.populate(objectMap);
    this.polymorphicMap.populate(polymorphicMap);
  }

  @override
  List<JsonKey<dynamic, dynamic>> get keys =>
      [stringMap, doubleMap, intMap, booleanMap, objectMap, polymorphicMap];
}

/// Create a [TestMaps] from in-memory data.
final maps = TestMaps.populated(
  stringMap: {'string1': 'value1', 'string2': 'value2'},
  doubleMap: {'double1': 2.5, 'double2': 3.5},
  intMap: {'int1': 3, 'int2': 4},
  booleanMap: {'bool1': false, 'bool2': true},
  objectMap: {'object1': object1, 'object2': object2},
  polymorphicMap: {'test1': polymorphicA, 'test2': polymorphicB},
);

/// A JSON representation of [maps].
final mapsJson = {
  'stringMap': {
    'string1': 'value1',
    'string2': 'value2',
  },
  'doubleMap': {
    'double1': 2.5,
    'double2': 3.5,
  },
  'intMap': {
    'int1': 3,
    'int2': 4,
  },
  'booleanMap': {
    'bool1': false,
    'bool2': true,
  },
  'objectMap': {
    'object1': objectJson1,
    'object2': objectJson2,
  },
  'polymorphicMap': {
    'test1': polymorphicJsonA,
    'test2': polymorphicJsonB,
  },
};

TestMaps.parse(mapsJson); /// Deserialize JSON into a [TestMaps].
maps.toJson(); /// Serialize a [TestMaps] into JSON.

Lists:

/// Example of a class that extends [Json] and uses [JsonKey]s to define
/// lists.
final class TestLists extends Json {
  final stringList = Json.stringList('myStringListKey');
  final doubleList = Json.doubleList('myDoubleListKey');
  final intList = Json.intList('myIntListKey');
  final boolList = Json.booleanList('myBoolListKey');
  final objectList = Json.objectList('myObjectListKey', TestObject.parser);
  final polymorphicList = 
    Json.polymorphicList('myPolymorphicListKey', TestPolymorphic.polymorphicParse);

  List<String> get myStringList => stringList.value;
  List<double> get myDoubleList => doubleList.value;
  List<int> get myIntList => intList.value;
  List<bool> get myBoolList => boolList.value;
  List<TestObject> get myObjectList => objectList.value;
  List<TestPolymorphic> get myPolymorphicList => polymorphicList.value;

  TestLists.parser() : super();

  TestLists.parse(super.json) : super.parse();

  TestLists.populated({
    required List<String> stringList,
    required List<double> doubleList,
    required List<int> intList,
    required List<bool> boolList,
    required List<TestObject> objectList,
    required List<TestPolymorphic> polymorphicList,
  }) : super() {
    this.stringList.populate(stringList);
    this.doubleList.populate(doubleList);
    this.intList.populate(intList);
    this.boolList.populate(boolList);
    this.objectList.populate(objectList);
    this.polymorphicList.populate(polymorphicList);
  }

  @override
  List<JsonKey<dynamic, dynamic>> get keys =>
      [stringList, doubleList, intList, boolList, objectList, polymorphicList];
}

/// Create a [TestLists] from in-memory data.
final lists = TestLists.populated(
  stringList: ['string1', 'string2'],
  doubleList: [2.5, 3.5],
  intList: [3, 4],
  boolList: [false, true],
  objectList: [object1, object2],
  polymorphicList: [polymorphicA, polymorphicB],
);

/// A JSON representation of [lists].
final listsJson = {
  'stringList': [
    'string1',
    'string2',
  ],
  'doubleList': [
    2.5,
    3.5,
  ],
  'intList': [
    3,
    4,
  ],
  'boolList': [
    false,
    true,
  ],
  'objectList': [
    objectJson1,
    objectJson2,
  ],
  'polymorphicList': [
    polymorphicJsonA,
    polymorphicJsonB,
  ],
};

TestLists.parse(listsJson); /// Deserialize JSON into a [TestLists].
lists.toJson(); /// Serialize a [TestLists] into JSON.

Complex data types:

/// Example of a class that extends [Json] and uses [JsonKey]s to define
/// complex data types.
final class TestAggregateObject extends Json {
  final objectJson = Json.object('myObjectKey', TestObject.parser);
  final mapsJson = Json.object('myMapsKey', TestMaps.parser);
  final listsJson = Json.object('myListsKey', TestLists.parser);

  TestObject get myObject => objectJson.value;
  TestMaps get myMaps => mapsJson.value;
  TestLists get myLists => listsJson.value;

  TestAggregateObject.parser() : super();

  TestAggregateObject.parse(super.json) : super.parse();

  TestAggregateObject.populated({
    required TestObject object,
    required TestMaps maps,
    required TestLists lists,
  }) : super() {
    objectJson.populate(object);
    mapsJson.populate(maps);
    listsJson.populate(lists);
  }

  @override
  List<JsonKey<dynamic, dynamic>> get keys => [objectJson, mapsJson, listsJson];
}

/// Create a [TestAggregateObject] from in-memory data.
final aggregateObject =
    TestAggregateObject.populated(object: object, maps: maps, lists: lists);

/// A JSON representation of [aggregateObject].
final aggregateObjectJson = {
  'object': objectJson,
  'maps': mapsJson,
  'lists': listsJson,
};

TestAggregateObject.parse(aggregateObjectJson); /// Deserialize JSON into a [TestAggregateObject].
aggregateObject.toJson(); /// Serialize a [TestAggregateObject] into JSON.

License/Disclaimer

See LICENSE