parse_json 1.0.0 copy "parse_json: ^1.0.0" to clipboard
parse_json: ^1.0.0 copied to clipboard

Type safe and simple JSON deserialization for Dart with no code generation or reflection.

parse_json #

License Pub.dev Github Stars

Type-safe JSON deserialization #

NO CODE GENERATION OR REFLECTION #

parse_json uses named parameters from your constructor to match JSON keys. It supports many features such as optionals, nested types, polymorphism/inheritance, and containers. It is also type-safe, meaning that if you try to deserialize a JSON object into a type that doesn't match, it will throw an error.

Getting Started #

Add the package to your pubspec.yaml:

For Flutter projects:

flutter pub add parse_json

For Dart projects:

dart pub add parse_json

Then import the package in your Dart code:

import 'package:parse_json/parse_json.dart';

Then match up a constructor's named parameters with your JSON data:

Data:

{
  "myString": "exampleStr",
  "myDouble": 12.5
}

Dart Code:

import 'package:parse_json/parse_json.dart';

final class ExampleObject {
  final String myString;
  final double myDouble;

  static const Map<String, Type> nonPrimitiveMembers = {};

  const ExampleObject({
    required this.myString,
    required this.myDouble,
  });

  factory ExampleObject.fromJson(Map<String, dynamic> json) => parse(json);
}

void main() {
  registerType<ExampleObject>(ExampleObject.new, ExampleObject.nonPrimitiveMembers);
}

Note that you do not need to name your variables the same as the JSON keys. You can specify a special constructor with named parameters that match the JSON keys, and use whatever names you like for your member variables. You also do not need to parse every member variable from JSON, in cases where you have derived data. Your members can also be non-final, but most of the examples in this documentation use final members for consistency.

Data:

{
  "myString": "exampleStr",
  "myDouble": 12.5
}

Dart Code:

import 'package:parse_json/parse_json.dart';

final class ExampleObject {
  // members with names different from those in the constructor
  final String name;
  final double originalHeight;

  // non-final member
  double currentHeight;

  static const Map<String, Type> nonPrimitiveMembers = {};

  // constructor we use for JSON
  const ExampleObject.json({
    required String myString,
    required double myDouble,
  }) : name = myString,
       originalHeight = myDouble,
       currentHeight = myDouble;

  // constructor we use for other stuff
  const ExampleObject(this.name, this.originalHeight, this.currentHeight);

  // helpful factory function for parsing and constructing from JSON
  factory ExampleObject.fromJson(Map<String, dynamic> json) => parse(json);
}

void main() {
  registerType<ExampleObject>(ExampleObject.new, ExampleObject.nonPrimitiveMembers);
}

At this point you have not used the nonPrimitiveMembers map for anything. This map is used for non-primitive types (Ones that are not String, int, double, bool, etc.). Here is an example of a class with non-primitive members:

Data:

{
  "myBoolList": [false, true, false],
  "myIntList": [12.5, 10.0, 5.0],
  "myFriend": {
    "myString": "friendStr",
    "myDouble": 10.5
  },
  "myOptionalFriend": ... another friend or this field could be omitted
  "friendList": [
    ... more friends
  ]
}

Dart Code:

import 'package:parse_json/parse_json.dart';

final class ExampleObject {
  final String myString;
  final double myDouble;

  static const Map<String, Type> nonPrimitiveMembers = {};

  const ExampleObject({
    required this.myString,
    required this.myDouble,
  });

  factory ExampleObject.fromJson(Map<String, dynamic> json) => parse(json);
}

final class ComplexExampleObject {
  final List<bool> myBoolList;
  final List<int> myIntList;
  final ExampleObject myFriend;
  final ExampleObject? myOptionalFriend;
  final List<ExampleObject> friendList;

  static const Map<String, Type> nonPrimitiveMembers = {
    'myFriend': ExampleObject,
    'myOptionalFriend': Optional<ExampleObject>,
    'friendList': List<ExampleObject>,
  };

  const ComplexExampleObject({
    required this.myString,
    required this.myDouble,
    required this.myFriend,
    required this.friendList,
    this.myOptionalFriend,
  });

  factory ComplexExampleObject.fromJson(Map<String, dynamic> json) => parse(json);
}

void main() {
  registerType<ExampleObject>(ExampleObject.new, ExampleObject.nonPrimitiveMembers);
  registerType<ComplexExampleObject>(ComplexExampleObject.new, ComplexExampleObject.nonPrimitiveMembers);
}

Example: #

Simple types: #

If your class does only consists of primitive members (such as String, int, double, bool, etc.), you don't have to do anything special. Just define your class. This is because those types translate directly from JSON. Notice that the named parameters of the constructor match the JSON keys.

import 'package:parse_json/parse_json.dart';

final class ExampleObject {
  final String myString;
  final double myDouble;
  final int myInt;
  final bool myBool;
  final String? myOptionalString;
  final int? myOptionalInt;

  static const Map<String, Type> nonPrimitiveMembers = {};

  const ExampleObject({
    required this.myString,
    required this.myDouble,
    required this.myInt,
    required this.myBool,
    this.myOptionalString,
    this.myOptionalInt,
  });

  factory ExampleObject.fromJson(Map<String, dynamic> json) => parse(json);
}

final object1 = ExampleObject(
  myString: 'exampleStr',
  myDouble: 12.5,
  myInt: 10,
  myBool: false,
);

final objectJson1 = {
  'myString': 'exampleStr',
  'myDouble': 12.5,
  'myInt': 10,
  'myBool': false,
};

final object2 = ExampleObject(
  myString: 'exampleStr2',
  myDouble: 102.5,
  myInt: -5,
  myBool: true,
  myOptionalString: 'hello',
  myOptionalInt: 42,
);

final objectJson2 = {
  'myString': 'exampleStr2',
  'myDouble': 102.5,
  'myInt': -5,
  'myBool': true,
  'myOptionalString': 'hello',
  'myOptionalInt': 42,
};

void main() {
  registerType<ExampleObject>(
      ExampleObject.new, ExampleObject.nonPrimitiveMembers);

  assert(ExampleObject.fromJson(objectJson1) == object1);
  assert(ExampleObject.fromJson(objectJson2) == object2);
}

Nested types: #

If your class has any members that are not primitive types, you need to register the type, and provide a map of the non-primitive members along with their JSON key (must match the constructor named parameter).

import 'package:parse_json/parse_json.dart';

final class SimpleObject extends Equatable {
  final String myString;
  final double myDouble;

  static const Map<String, Type> nonPrimitiveMembers = {};

  const SimpleObject({
    required this.myString,
    required this.myDouble,
  });

  factory SimpleObject.fromJson(Map<String, dynamic> json) => parse(json);

  @override
  List<Object?> get props => [myString, myDouble];
}

final class ComplexObject extends Equatable {
  final List<SimpleObject> exampleList;
  final Map<String, SimpleObject> exampleMap;
  final SimpleObject? optionalExampleObject;
  final SimpleObject exampleObject;

  static const Map<String, Type> nonPrimitiveMembers = {
    'exampleList': List<SimpleObject>,
    'exampleMap': Map<String, SimpleObject>,
    'optionalExampleObject': Optional<SimpleObject>,
    'exampleObject': SimpleObject,
  };

  const ComplexObject({
    required this.exampleList,
    required this.exampleMap,
    required this.exampleObject,
    this.optionalExampleObject,
  });

  factory ComplexObject.fromJson(Map<String, dynamic> json) => parse(json);

  @override
  List<Object?> get props => [
    exampleList, 
    exampleMap, 
    exampleObject, 
    optionalExampleObject,
  ];
}

final object1 = SimpleObject(myString: 'exampleStr', myDouble: 12.5);
final objectJson1 = {'myString': 'exampleStr', 'myDouble': 12.5};

final object2 = SimpleObject(myString: 'exampleStr2', myDouble: 102.5);
final objectJson2 = {'myString': 'exampleStr2', 'myDouble': 102.5};

final complexObject = ComplexObject(
  exampleList: [object1, object2],
  exampleMap: {'object1': object1, 'object2': object2},
  exampleObject: object1,
);

final complexObjectJson = {
  'exampleList': [objectJson1, objectJson2],
  'exampleMap': {'object1': objectJson1, 'object2': objectJson2},
  'exampleObject': objectJson1,
};

void main() {
  registerType<SimpleObject>(
      SimpleObject.new, SimpleObject.nonPrimitiveMembers);
  registerType<ComplexObject>(
      ComplexObject.new, ComplexObject.nonPrimitiveMembers);

  assert(SimpleObject.fromJson(objectJson1) == object1);
  assert(SimpleObject.fromJson(objectJson2) == object2);
  assert(ComplexObject.fromJson(complexObjectJson) == complexObject);
}

Inheritance/Polymorphic types: #

With polymorphic base types, in order to register them you need to use registerPolymorphicType instead of registerType. You also need to provide a polymorphicKey and a polymorphicId for each subclass. The polymorphicKey is the key in the JSON that will be used to determine the type of the object. The polymorphicId is the value of the polymorphicKey that will be used to determine the type of the object. The polymorphicId must be unique for each subclass.

import 'package:parse_json/parse_json.dart';

final class BaseClass extends Equatable {
  static const polymorphicKey = 'type';
  
  final String myString;
  final double myDouble;

  static const Map<String, Type> nonPrimitiveMembers = {};

  const BaseClass({
    required this.myString,
    required this.myDouble,
  });

  factory BaseClass.fromJson(Map<String, dynamic> json) => parse(json);

  @override
  List<Object?> get props => [myString, myDouble];
}

final class SubClassA extends BaseClass {
  static const polymorphicId = 'A';

  final int myInt;

  static const Map<String, Type> nonPrimitiveMembers = {};

  const SubClassA({
    required super.myString,
    required super.myDouble,
    required this.myInt,
  }) : super();

  factory SubClassA.fromJson(Map<String, dynamic> json) => parse(json);

  @override
  List<Object?> get props => [...super.props, myInt];
}

final class SubClassB extends BaseClass {
  static const polymorphicId = 'B';

  final ExampleObject myExampleObject;

  static const Map<String, Type> nonPrimitiveMembers = {
    'myExampleObject': ExampleObject,
  };

  const SubClassB({
    required super.myString,
    required super.myDouble,
    required this.myExampleObject,
  }) : super();

  factory SubClassB.fromJson(Map<String, dynamic> json) => parse(json);

  @override
  List<Object?> get props => [...super.props, myExampleObject];
}

final objectA = SubClassA(myString: 'exampleStr', myDouble: 12.5, myInt: 10);
final objectJsonA = {
  'type': 'A',
  'myString': 'exampleStr',
  'myDouble': 12.5,
  'myInt': 10,
};

final objectB = SubClassB(
  myString: 'exampleStr2',
  myDouble: 102.5,
  myExampleObject: ExampleObject(
    myString: 'exampleStr',
    myDouble: 12.5,
    myInt: 10,
    myBool: false,
  ),
);

final objectJsonB = {
  'type': 'B',
  'myString': 'exampleStr2',
  'myDouble': 102.5,
  'myExampleObject': {
    'myString': 'exampleStr',
    'myDouble': 12.5,
    'myInt': 10,
    'myBool': false,
  },
};

final baseClass = BaseClass(myString: 'exampleStr', myDouble: 12.5);
final baseClassJson = {'myString': 'exampleStr', 'myDouble': 12.5};

void main() {
  registerType<ExampleObject>(ExampleObject.new, ExampleObject.nonPrimitiveMembers); // Defined elsewhere
  
  registerType<SubClassA>(SubClassA.new, SubClassA.nonPrimitiveMembers);
  registerType<SubClassB>(SubClassB.new, SubClassB.nonPrimitiveMembers);
  
  registerPolymorphicType<BaseClass>(
    BaseClass.polymorphicKey,
    {
      SubClassA.polymorphicId: SubClassA,
      SubClassB.polymorphicId: SubClassB,
    },
    (BaseClass.new, BaseClass.nonPrimitiveMembers),
  );

  assert(SubClassA.fromJson(objectJsonA) == objectA);
  assert(SubClassB.fromJson(objectJsonB) == objectB);
  assert(BaseClass.fromJson(baseClassJson) == baseClass);

  // polymorphism
  assert(BaseClass.fromJson(objectJsonA) == objectA);
  assert(BaseClass.fromJson(objectJsonB) == objectB);
}

License/Disclaimer #

See LICENSE

7
likes
0
points
67
downloads

Publisher

verified publisherjohn-siegel.com

Weekly Downloads

Type safe and simple JSON deserialization for Dart with no code generation or reflection.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

equatable

More

Packages that depend on parse_json