This package allows programmers to annotate Dart objects in order to Serialize / Deserialize them to / from JSON

Overview

Build Status pub package

This package allows programmers to annotate Dart objects in order to Serialize / Deserialize them to / from JSON.

Why?

  • Compatible with all target platforms for Dart language, NO dependency on dart:mirrors, one of the reasons is described here.
  • No need to extend your classes from any mixins/base/abstract classes to keep code leaner
  • No enforced private constructors, do not require any constructors at all for your data-only classes
  • No magic _$ prefixes, No enforced static fields in your classes
  • Clean and simple setup, transparent and straight-forward usage with no heavy maintenance
  • Inspired by json2typescript, serde, gson, feature parity with highly popular Java Jackson and only 4 annotations to remember to cover all possible use cases.
  • No extra boilerplate, no messy extra *.g.dart files per each meaningful file (single root-level file which contains all of the generated code)
  • Complementary adapters full control over the process when you strive for maximum flexibility.
  • Configuration over code brings predictability to your codebase, while reducing overall amount of code to read / maintain
  • Because Serialization/Deserialization is NOT a responsibility of your Model classes.

Dart classes reflection mechanism is based on reflectable library. This means "extended types information" is auto-generated out of existing Dart program guided by the annotated classes only, as the result types information is accessible at runtime, at a reduced cost.

Basic setup

Please add the following dependencies to your pubspec.yaml:

dependencies:
  dart_json_mapper:
dev_dependencies:
  build_runner:

Say, you have a dart program main.dart having some classes intended to be traveling to JSON and back.

  • First thing you should do is to put @jsonSerializable annotation on each of those classes
  • Next step is to auto generate main.mapper.g.dart file. And afterwards import that file into main.dart

lib/main.dart

import 'package:dart_json_mapper/dart_json_mapper.dart' show JsonMapper, jsonSerializable, JsonProperty;

import 'main.mapper.g.dart' show initializeJsonMapper;

@jsonSerializable // This annotation let instances of MyData travel to/from JSON
class MyData {
  int a = 123;

  @JsonProperty(ignore: true)
  bool b;

  @JsonProperty(name: 'd')
  String c;

  MyData(this.a, this.b, this.c);
}

void main() {
  initializeJsonMapper();
  
  print(JsonMapper.serialize(MyData(456, true, "yes")));
}

output:

{ 
  "a": 456,
  "d": "yes"
}

Go ahead and create / update build.yaml file in your project root directory with the following snippet:

targets:
  $default:
    builders:
      dart_json_mapper:
        generate_for:
        # here should be listed entry point files having 'void main()' function
          - lib/main.dart

      # This part is needed to tell original reflectable builder to stay away
      # it overrides default options for reflectable builder to an **empty** set of files
      reflectable:
        generate_for:
          - no/files

Now run the code generation step with the root of your package as the current directory:

> dart run build_runner build --delete-conflicting-outputs

You'll need to re-run code generation each time you are making changes to lib/main.dart So for development time, use watch like this

> dart run build_runner watch --delete-conflicting-outputs

Each time you modify your project code, all *.mapper.g.dart files will be updated as well.

  • Next step is to add *.mapper.g.dart to your .gitignore
  • And this is it, you are all set and ready to go. Happy coding!

Format DateTime / num types

In order to format DateTime or num instance as a JSON string, it is possible to provide intl based formatting patterns.

DateTime

@JsonProperty(converterParams: {'format': 'MM-dd-yyyy H:m:s'})
DateTime lastPromotionDate = DateTime(2008, 05, 13, 22, 33, 44);

@JsonProperty(converterParams: {'format': 'MM/dd/yyyy'})
DateTime hireDate = DateTime(2003, 02, 28);

output:

{
"lastPromotionDate": "05-13-2008 22:33:44",
"hireDate": "02/28/2003"
}

num

@JsonProperty(converterParams: {'format': '##.##'})
num salary = 1200000.246;

output:

{
"salary": "1200000.25"
}

As well, it is possible to utilize converterParams map to provide custom parameters to your custom converters.

Get or Set fields

When relying on Dart getters / setters, no need to annotate them. But when you have custom getter / setter methods, you should provide annotations for them.

(json); // then expect(instance.name, 'Bob'); expect(instance.getLastName(), 'Marley'); // when final targetJson = JsonMapper.serialize(instance, SerializationOptions(indent: '')); // then expect(targetJson, json);">
@jsonSerializable
class AllPrivateFields {
  String? _name;
  String? _lastName;

  set name(dynamic value) {
    _name = value;
  }

  String? get name => _name;

  @JsonProperty(name: 'lastName')
  void setLastName(dynamic value) {
    _lastName = value;
  }

  @JsonProperty(name: 'lastName')
  String? getLastName() => _lastName;
}

// given
final json = '''{"name":"Bob","lastName":"Marley"}''';

// when
final instance = JsonMapper.deserialize<AllPrivateFields>(json);

// then
expect(instance.name, 'Bob');
expect(instance.getLastName(), 'Marley');

// when
final targetJson = JsonMapper.serialize(instance, SerializationOptions(indent: ''));

// then
expect(targetJson, json);

Example with immutable class

@jsonSerializable
enum Color { red, blue, green, brown, yellow, black, white }

@jsonSerializable
class Car {
    @JsonProperty(name: 'modelName')
    String model;
    
    Color color;
    
    @JsonProperty(ignore: true)
    Car replacement;
    
    Car(this.model, this.color);
}

@jsonSerializable
class Immutable {
    final int id;
    final String name;
    final Car car;
    
    const Immutable(this.id, this.name, this.car);
}

print(
  JsonMapper.serialize(
    Immutable(1, 'Bob', Car('Audi', Color.green))
  )
);

output:

{
 "id": 1,
 "name": "Bob",
 "car": {
  "modelName": "Audi",
  "color": "green"
 }
}

Constructor parameters

Sometimes you don't really care or don't want to store some json property as a dedicated class field, but instead, you would like to use it's value in constructor to calculate other class properties. This way you don't have a convenience to annotate a class field, but you could utilize constructor parameter for that.

With the input JSON like this:

{"LogistikTeileInOrdnung":"true"}

You could potentially have a class like this:

@jsonSerializable
class BusinessObject {
  final bool logisticsChecked;
  final bool logisticsOK;

  BusinessObject()
      : logisticsChecked = false,
        logisticsOK = true;

  @jsonConstructor
  BusinessObject.fromJson(
      @JsonProperty(name: 'LogistikTeileInOrdnung') String processed)
      : logisticsChecked = processed != null && processed != 'null',
        logisticsOK = processed == 'true';
}

Unmapped properties

If you are looking for an alternative to Java Jackson @JsonAnySetter / @JsonAnyGetter It is possible to configure the same scenario as follows:

(json); // then expect(instance.name, 'Bob'); expect(instance._extraPropsMap['name'], null); expect(instance._extraPropsMap['extra1'], 1); expect(instance._extraPropsMap['extra2'], 'xxx');">
@jsonSerializable
class UnmappedProperties {
  String name;

  Map<String, dynamic> _extraPropsMap = {};

  @jsonProperty
  void unmappedSet(String name, dynamic value) {
    _extraPropsMap[name] = value;
  }

  @jsonProperty
  Map<String, dynamic> unmappedGet() {
    return _extraPropsMap;
  }
}

// given
final json = '''{"name":"Bob","extra1":1,"extra2":"xxx"}''';

// when
final instance = JsonMapper.deserialize<UnmappedProperties>(json);

// then
expect(instance.name, 'Bob');
expect(instance._extraPropsMap['name'], null);
expect(instance._extraPropsMap['extra1'], 1);
expect(instance._extraPropsMap['extra2'], 'xxx');

Iterable types

Since Dart language has no possibility to create typed iterables dynamically, it's a bit of a challenge to create exact typed lists/sets/etc via reflection approach. Those types has to be declared explicitly.

For example List() will produce List type which can't be directly set to the concrete target field List for instance. So obvious workaround will be to cast List => List , which can be performed as List ().cast () .

Basic iterable based generics using Dart built-in types like List , List , List , List , Set , Set , Set , Set , etc. supported out of the box.

In order to do so, we'll use Value Decorator Functions inspired by Decorator pattern.

To solve this we have a few options:

Provide value decorator functions manually

  • As a global adapter

    >(json); final myCarsSet = JsonMapper.deserialize >(json);">
    JsonMapper().useAdapter(JsonMapperAdapter(
      valueDecorators: {
        typeOf<List<Car>>(): (value) => value.cast<Car>(),
        typeOf<Set<Car>>(): (value) => value.cast<Car>()
      })
    );
    
    final json = '[{"modelName": "Audi", "color": "green"}]';
    final myCarsList = JsonMapper.deserialize<List<Car>>(json);
    final myCarsSet = JsonMapper.deserialize<Set<Car>>(json);
  • As an class inline code

    @jsonSerializable
    @Json(valueDecorators: CarsContainer.valueDecorators)
    class CarsContainer {
      static Map<Type, ValueDecoratorFunction> valueDecorators() =>
          {
            typeOf<List<Car>>(): (value) => value.cast<Car>(),
            typeOf<Set<Car>>(): (value) => value.cast<Car>()
          };
    
      List<Car> myCarsList;
      Set<Car> myCarsSet;
    }

Rely on builder to generate global adapter having value decorator functions automatically

Builder will scan project code during build pass and will generate value decorator functions for all annotated public classes in advance.

For custom iterable types like List / Set we don't have to provide value decorators as showed in a code snippet below, thanks to the Builder

>(json); final myCarsSet = JsonMapper.deserialize >(json);">
final json = '[{"modelName": "Audi", "color": "green"}]';
final myCarsList = JsonMapper.deserialize<List<Car>>(json);
final myCarsSet = JsonMapper.deserialize<Set<Car>>(json);

For custom iterable types like HashSet / UnmodifiableListView we should configure Builder to support that.

OR an easy case

When you are able to pre-initialize your Iterables with an empty instance, like on example below, you don't need to mess around with value decorators.

(json); // then expect(target.list, TypeMatcher >()); expect(target.list.first, TypeMatcher ()); expect(target.list.length, 2); expect(target.set, TypeMatcher >()); expect(target.set.first, TypeMatcher ()); expect(target.set.length, 2);">
@jsonSerializable
class Item {}

@jsonSerializable
class IterablesContainer {
  List<Item> list = [];
  Set<Item> set = {};
}

// given
final json = '''{"list":[{}, {}],"set":[{}, {}]}''';

// when
final target = JsonMapper.deserialize<IterablesContainer>(json);

// then
expect(target.list, TypeMatcher<List<Item>>());
expect(target.list.first, TypeMatcher<Item>());
expect(target.list.length, 2);

expect(target.set, TypeMatcher<Set<Item>>());
expect(target.set.first, TypeMatcher<Item>());
expect(target.set.length, 2);

List of Lists of Lists ...

Using value decorators, it's possible to configure nested lists of virtually any depth.

(json)!; // then expect(target.lists?.length, 2); expect(target.lists?.first.length, 2); expect(target.lists?.last.length, 3); expect(target.lists?.first.first, TypeMatcher ()); expect(target.lists?.last.first, TypeMatcher ());">
@jsonSerializable
class Item {}

@jsonSerializable
@Json(valueDecorators: ListOfLists.valueDecorators)
class ListOfLists {
  static Map<Type, ValueDecoratorFunction> valueDecorators() =>
      {
        typeOf<List<List<Item>>>(): (value) => value.cast<List<Item>>(),
        typeOf<List<Item>>(): (value) => value.cast<Item>()
      };
  
  List<List<Item>>? lists;
}

// given
final json = '''{
 "lists": [
   [{}, {}],
   [{}, {}, {}]
 ]
}''';

// when
final target = JsonMapper.deserialize<ListOfLists>(json)!;

// then
expect(target.lists?.length, 2);
expect(target.lists?.first.length, 2);
expect(target.lists?.last.length, 3);
expect(target.lists?.first.first, TypeMatcher<Item>());
expect(target.lists?.last.first, TypeMatcher<Item>());

Enum types

Enum construction in Dart has a specific meaning, and has to be treated accordingly.

Generally, we always have to bear in mind following cases around Enums:

  • Your own Enums declared as part of your program code, thus they can be annotated.

    So whenever possible, you should annotate your Enum declarations as follows

    @jsonSerializable
    enum Color { red, blue, green, brown, yellow, black, white }
  • Standalone Enums from third party packages, they can not be annotated.

    So you should register those enums via adapter as follows:

    import 'package:some_package' show ThirdPartyEnum, ThirdPartyEnum2;
    
    JsonMapper().useAdapter(
        JsonMapperAdapter(enumValues: {
            ThirdPartyEnum: ThirdPartyEnum.values,
            ThirdPartyEnum2: ThirdPartyEnum2.values
        })
    );

    Enum.values refers to a list of all possible enum values, it's a handy built in capability of all enum based types. Without providing all values it's not possible to traverse it's values properly.

There are few enum converters provided out of the box:

  • enumConverterShort produces values like: ["red", "blue", "green"], unless custom value mappings provided
  • enumConverter produces values like: ["Color.red", "Color.blue", "Color.green"]
  • enumConverterNumeric produces values like: [0, 1, 2]

Default converter for all enums is enumConverterShort

In case we would like to make a switch globally to the different one, or even custom converter for all enums

// lib/main.dart
void main() {
  initializeJsonMapper(adapters: [
   JsonMapperAdapter(converters: {Enum: enumConverter})
  ]);
}

Enums having String / num values

What are the options if you would like to serialize / deserialize Enum values as custom values?

  • Wrap each enum as a class, to reflect it's values as something different
  • Use other libraries for sealed classes like SuperEnum, Freezed

OR

While registering standalone enums via adapter it is possible to specify value mapping for each enum, alongside defaultValue which will be used during deserialization of unknown Enum values.

import 'package:some_package' show ThirdPartyEnum, ThirdPartyEnum2, ThirdPartyEnum3;

JsonMapper().useAdapter(
    JsonMapperAdapter(enumValues: {
        ThirdPartyEnum: ThirdPartyEnum.values,
       ThirdPartyEnum2: EnumDescriptor(
                            values: ThirdPartyEnum2.values,
                           mapping: <ThirdPartyEnum2, String>{
                                      ThirdPartyEnum2.A: 'AAA',
                                      ThirdPartyEnum2.B: 'BBB',
                                      ThirdPartyEnum2.C: 'CCC'
                                    }
                        ),
       ThirdPartyEnum3: EnumDescriptor(
                            values: ThirdPartyEnum3.values,
                      defaultValue: ThirdPartyEnum3.A,
                           mapping: <ThirdPartyEnum3, num>{
                                      ThirdPartyEnum3.A: -1.2,
                                      ThirdPartyEnum3.B: 2323,
                                      ThirdPartyEnum3.C: 1.2344
                                    }
                        )
    })
);

So this way, you'll still operate on classic / pure Dart enums and with all that sending & receiving them as mapped values. After registering those enums once, no matter where in the code you'll use them later they will be handled according to the configuration given w/o annotating them beforehand.

Inherited classes derived from abstract / base class

Please use complementary @Json(discriminatorProperty: 'type') annotation for abstract or base class to specify which class field(type in this snippet below) will be used to store a value for distinguishing concrete subclass type.

Please use complementary @Json(discriminatorValue: ) annotation for subclasses derived from abstract or base class. If this annotation omitted, class name will be used as discriminatorValue

This ensures, that dart-json-mapper will be able to reconstruct the object with the proper type during deserialization process.

()); expect(target.businesses[1], TypeMatcher ());">
@jsonSerializable
enum BusinessType { Private, Public }

@jsonSerializable
@Json(discriminatorProperty: 'type')
abstract class Business {
  String? name;
  BusinessType? type;
}

@jsonSerializable
@Json(discriminatorValue: BusinessType.Private)
class Hotel extends Business {
  int stars;

  Hotel(this.stars);
}

@jsonSerializable
@Json(discriminatorValue: BusinessType.Public)
class Startup extends Business {
  int userCount;

  Startup(this.userCount);
}

@jsonSerializable
class Stakeholder {
  String fullName;
  List<Business> businesses = [];

  Stakeholder(this.fullName, this.businesses);
}

// given
final jack = Stakeholder("Jack", [Startup(10), Hotel(4)]);

// when
final String json = JsonMapper.serialize(jack);
final Stakeholder target = JsonMapper.deserialize(json);

// then
expect(target.businesses[0], TypeMatcher<Startup>());
expect(target.businesses[1], TypeMatcher<Hotel>());

Classes enhanced with Mixins derived from abstract class

Similar configuration as above also works well for class mixins

(targetJson); // then expect(targetJson, json); expect(target, TypeMatcher ()); expect(target.b, TypeMatcher ());">
@Json(discriminatorProperty: 'type')
@jsonSerializable
abstract class A {}

@jsonSerializable
mixin B on A {}

@jsonSerializable
class C extends A with B {}

@jsonSerializable
class MixinContainer {
  final Set<int> ints;
  final B b;

  const MixinContainer(this.ints, this.b);
}

// given
final json = r'''{"ints":[1,2,3],"b":{"type":"C"}}''';
final instance = MixinContainer({1, 2, 3}, C());

// when
final targetJson = JsonMapper.serialize(instance);
final target = JsonMapper.deserialize<MixinContainer>(targetJson);

// then
expect(targetJson, json);
expect(target, TypeMatcher<MixinContainer>());
expect(target.b, TypeMatcher<C>());

Serialization template

In case you already have an instance of huge JSON Map object and portion of it needs to be surgically updated, then you can pass your Map instance as a template parameter for SerializationOptions

// given
final template = {'a': 'a', 'b': true};

// when
final json = JsonMapper.serialize(Car('Tesla S3', Color.black),
  SerializationOptions(indent: '', template: template));

// then
expect(json,
  '''{"a":"a","b":true,"modelName":"Tesla S3","color":"black"}''');

Deserialization template

In case you need to deserialize specific Map type then you can pass typed instance of it as a template parameter for DeserializationOptions.

Since typed Map instance cannot be created dynamically due to Dart language nature, so you are providing ready made instance to use for deserialization output.

{})); // then expect(target, TypeMatcher >()); expect(target.containsKey(Color.black), true); expect(target.containsKey(Color.blue), true); expect(target[Color.black], 1); expect(target[Color.blue], 2);">
// given
final json = '{"black":1,"blue":2}';

// when
final target = JsonMapper.deserialize(
          json, DeserializationOptions(template: <Color, int>{}));

// then
expect(target, TypeMatcher<Map<Color, int>>());
expect(target.containsKey(Color.black), true);
expect(target.containsKey(Color.blue), true);
expect(target[Color.black], 1);
expect(target[Color.blue], 2);

Name casing styles [Pascal, Kebab, Snake, SnakeAllCaps]

Assuming your Dart code is following Camel case style, but that is not always true for JSON models, they could follow one of those popular - Pascal, Kebab, Snake, SnakeAllCaps styles, right?

That's why we need a smart way to manage that, instead of hand coding each property using @JsonProperty(name: ...) it is possible to pass CaseStyle parameter to serialization / deserialization methods OR specify this preference on a class level using @Json(caseStyle: CaseStyle.kebab).

(json); // then expect(instance.mainTitle, 'title'); expect(instance.hasMainProperty, true); expect(instance.primaryColor, Color.grayMetallic);">
@jsonSerializable
enum Color { red, blue, gray, grayMetallic, green, brown, yellow, black, white }

@jsonSerializable
@Json(caseStyle: CaseStyle.kebab)
class NameCaseObject {
  String mainTitle;
  bool hasMainProperty;
  Color primaryColor;

  NameCaseObject({
      this.mainTitle,
      this.hasMainProperty,
      this.primaryColor = Color.grayMetallic});
}

/// Serialization

// given
final instance = NameCaseObject(mainTitle: 'title', hasMainProperty: true);
// when
final json = JsonMapper.serialize(instance, SerializationOptions(indent: ''));
// then
expect(json, '''{"main-title":"title","has-main-property":true,"primary-color":"gray-metallic"}''');

/// Deserialization

// given
final json = '''{"main-title":"title","has-main-property":true,"primary-color":"gray-metallic"}''';
// when
final instance = JsonMapper.deserialize<NameCaseObject>(json);
// then
expect(instance.mainTitle, 'title');
expect(instance.hasMainProperty, true);
expect(instance.primaryColor, Color.grayMetallic);

Nesting configuration

In case if you need to operate on particular portions of huge JSON object and you don't have a true desire to reconstruct the same deep nested JSON objects hierarchy with corresponding Dart classes. This section is for you!

Say, you have a json similar to this one

{
  "root": {
    "foo": {
      "bar": {
        "baz": {
          "items": [
            "a",
            "b",
            "c"
          ]
        }
      }
    }
  }
}          

And with code similar to this one

@jsonSerializable
@Json(name: 'root/foo/bar')
class BarObject {
  @JsonProperty(name: 'baz/items')
  List<String> items;

  BarObject({this.items});
}

// when
final instance = JsonMapper.deserialize<BarObject>(json);

// then
expect(instance.items.length, 3);
expect(instance.items, ['a', 'b', 'c']);

you'll have it done nice and quick.

@Json(name: 'root/foo/bar') provides a root nesting for the entire annotated class, this means all class fields will be nested under this 'root/foo/bar' path in Json.

@JsonProperty(name: 'baz/items') provides a field nesting relative to the class root nesting

name is compliant with RFC 6901 JSON pointer

Relative path reference to parent field from nested object "../id"

When it's handy to refer to the parent fields values, it's possible to use path like notation "../"

[
  {"id":1,"name":"category1","products":[
         {"id":3629,"name":"Apple","features":[{"id":9,"name":"Red Color"}]},
         {"id":5674,"name":"Banana"}]},
  {"id":2,"name":"category2","products":[
         {"id":7834,"name":"Car"},
         {"id":2386,"name":"Truck"}
   ]}
]
@jsonSerializable
class Feature {
  @JsonProperty(name: '../../id')
  num categoryId;

  @JsonProperty(name: '../id')
  num productId;

  num id;
  String name;

  Feature({this.name, this.id});
}

@jsonSerializable
class Product {
  @JsonProperty(name: '../id')
  num categoryId;

  num id;
  String name;

  @JsonProperty(ignoreIfNull: true)
  List<Feature> features;

  Product({this.name, this.id, this.features});
}

@jsonSerializable
class ProductCategory {
  num id;
  String name;
  List<Product> products;

  ProductCategory({this.id, this.name, this.products});
}

Relative path reference to parent itself from nested object ".."

In some cases objects need to interact with their (owning) parent object. The easiest pattern is to add a referencing field for the parent which is initialized during construction of the child object. The path notation ".." supports this pattern:

@jsonSerializable
class Parent {
  String? lastName;
  List<Child> children = [];
}

@jsonSerializable
class Child {
  String? firstName;

  @JsonProperty(name: '..')
  Parent parent;

  Child(this.parent);
}

You are now able to deserialize the following structure:

{
  "lastName": "Doe",
  "children": [
    {"firstName": "Eve"},
    {"firstName": "Bob"},
    {"firstName": "Alice"}
]}

and each Child object will have a reference on it's parent. And this parent field will not leak out to the serialized JSON object

Value injection

Sometimes you have to inject certain values residing outside of a JSON string into the target deserialized object. Using the JsonProperty.inject flag, one may do so.

class Outside {}

@jsonSerializable
class Inside {
  String? foo;

  @JsonProperty(name: 'data/instance', inject: true)
  Outside? outside;
}

You may then inject the values in the deserialize method:

{
  "foo": "Bar"
}
Outside outsideInstance = Outside();
final target = JsonMapper.deserialize<Inside>(json,
  DeserializationOptions(injectableValues: {'data': {'instance': outsideInstance}})!;

Name aliases configuration

For cases when aliasing technique is desired, it's possible to optionally merge / route many json properties into one class field. First name from the list is treated as primary i.e. used for serialization direction. The rest of items are treated as aliases joined by the ?? operation.

@jsonSerializable
class FieldAliasObject {
  // same as => alias ?? fullName ?? name
  @JsonProperty(name: ['alias', 'fullName', 'name'])
  final String name;

  const FieldAliasObject({
    this.name,
  });
}

Schemes

Scheme - is a set of annotations associated with common scheme id. This enables the possibility to map a single Dart class to many different JSON structures.

This approach usually useful for distinguishing [DEV, PROD, TEST, ...] environments, w/o producing separate Dart classes for each environment.

enum Scheme { A, B }

@jsonSerializable
@Json(name: 'default')
@Json(name: '_', scheme: Scheme.B)
@Json(name: 'root', scheme: Scheme.A)
class Object {
  @JsonProperty(name: 'title_test', scheme: Scheme.B)
  String title;

  Object(this.title);
}

// given
final instance = Object('Scheme A');
// when
final json = JsonMapper.serialize(instance, SerializationOptions(indent: '', scheme: Scheme.A));
// then
expect(json, '''{"root":{"title":"Scheme A"}}''');

// given
final instance = Object('Scheme B');
// when
final json = JsonMapper.serialize(instance, SerializationOptions(indent: '', scheme: Scheme.B));
// then
expect(json, '''{"_":{"title_test":"Scheme B"}}''');

// given
final instance = Object('No Scheme');
// when
final json = JsonMapper.serialize(instance, SerializationOptions(indent: ''));
// then
expect(json, '''{"default":{"title":"No Scheme"}}''');

Objects flattening

Consider a paginated API which returns a page of results along with pagination metadata that identifies how many results were requested, how far into the total set of results we are looking at, and how many results exist in total. If we are paging through a total of 1053 results 100 at a time, the third page may look like this:

{
  "limit": 100,
  "offset": 200,
  "total": 1053,
  "users": [
    {"id": "49824073-979f-4814-be10-5ea416ee1c2f", "username": "john_doe"},
    ...
  ]
}

This same scheme with limit and offset and total fields may be shared across lots of different API queries. For example we may want paginated results when querying for users, for issues, for projects, etc.

In this case it can be convenient to factor the common pagination metadata fields into a reusable Pagination shared class that can be flattened & blended into each API response object.

@jsonSerializable
class Pagination {
  num? limit;
  num? offset;
  num? total;
}

@jsonSerializable
class UsersPage {
  @JsonProperty(flatten: true)
  Pagination? pagination;

  List<User>? users;
}

If it's desired to define common prefix for flattened fields @JsonProperty.name attribute could be utilized for that alongside with flatten: true attribute.

Case style could be defined as usual, on a class level @Json(caseStyle: CaseStyle.snake) and/or global scope with DeserializationOptions(caseStyle: CaseStyle.kebab) and SerializationOptions(caseStyle: CaseStyle.kebab) If omitted, CaseStyle.camel is used by default.

@jsonSerializable
class Pagination {
  num? limit;
  num? offset;
  num? total;
}

@jsonSerializable
@Json(caseStyle: CaseStyle.snake)
class UsersPage {
  @JsonProperty(name: 'pagination', flatten: true)
  Pagination? pagination;

  List<User>? users;
}

This will output

{
  "pagination_limit": 100,
  "pagination_offset": 200,
  "pagination_total": 1053,
  "users": [
    {"id": "49824073-979f-4814-be10-5ea416ee1c2f", "username": "john_doe"},
    ...
  ]
}

Objects cloning

If you are wondering how to deep-clone Dart Objects, or even considering using libraries like Freezed to accomplish that, then this section probably will be useful for you

// given
final car = Car('Tesla S3', Color.black);

// when
final cloneCar = JsonMapper.copy(car);

// then
expect(cloneCar == car, false);
expect(cloneCar.color == car.color, true);
expect(cloneCar.model == car.model, true);

Or if you would like to override some properties for the clonned object instance

// given
final car = Car('Tesla S3', Color.black);

// when
final cloneCar = JsonMapper.copyWith(car, {'color': 'blue'}); // overriding Black by Blue

// then
expect(cloneCar == car, false);
expect(cloneCar.color, Color.blue);
expect(cloneCar.model, car.model);

Custom types

For the very custom types, specific ones, or doesn't currently supported by this library, you can provide your own custom Converter class per each custom runtimeType.

/// Abstract class for custom converters implementations
abstract class ICustomConverter<T> {
  dynamic toJSON(T object, SerializationContext context);
  T fromJSON(dynamic jsonValue, DeserializationContext context);
}

All you need to get going with this, is to implement this abstract class

class CustomStringConverter implements ICustomConverter<String> {
  const CustomStringConverter() : super();

  @override
  String fromJSON(dynamic jsonValue, DeserializationContext context) {
    return jsonValue;
  }

  @override
  dynamic toJSON(String object, SerializationContext context) {
    return '_${object}_';
  }
}

And register it afterwards, if you want to have it applied for all occurrences of specified type

JsonMapper().useAdapter(JsonMapperAdapter(
  converters: {
    String: CustomStringConverter()
  })
);

OR use it individually on selected class fields, via @JsonProperty annotation

@JsonProperty(converter: CustomStringConverter())
String title;

Annotations

  • @JsonSerializable() or @jsonSerializable for short, It's a required marker annotation for Class, Mixin, or Enum declarations. Use it to mark all the Dart objects you'd like to be traveling to / from JSON
    • Has NO params
  • @JsonConstructor() or @jsonConstructor for short, It's an optional constructor only marker annotation. Use it to mark specific Dart class constructor you'd like to be used during deserialization.
    • scheme dynamic Scheme marker to associate this meta information with particular mapping scheme
  • @Json(...) It's an optional annotation for class declaration, describes a Dart object to JSON Object mapping. Why it's not a @JsonObject()? just for you to type less characters 😄
    • name Defines RFC 6901 JSON pointer, denotes the json Object root name/path to be used for mapping. Example: 'foo', 'bar', 'foo/bar/baz'
    • caseStyle The most popular ways to combine words into a single string. Based on assumption: That all Dart class fields initially given as CaseStyle.camel
    • discriminatorProperty Defines a class property to be used as a source of truth for discrimination logic in a hierarchy of inherited classes. Usually used on annotation of [abstract] class
    • discriminatorValue Defines a custom override value for a discriminator. Usually used on annotations of subclasses, to distinguish it from each other. Default value: annotated class name
    • valueDecorators Provides an inline way to specify a static function which will return a Map of value decorators, to support type casting activities for Map , and other generic Iterables instead of global adapter approach
    • ignoreNullMembers If set to true Null class members will be excluded from serialization process
    • ignoreDefaultMembers If set to true Class members having default value will be excluded from serialization process
    • processAnnotatedMembersOnly If set to true Only annotated class members will be processed
    • allowCircularReferences As of int type. Allows certain number of circular object references during serialization.
    • scheme dynamic Scheme marker to associate this meta information with particular mapping scheme
  • @JsonProperty(...) It's an optional class member annotation, describes JSON Object property mapping.
    • name Defines RFC 6901 JSON pointer, denotes the name/path/aliases to be used for property mapping relative to the class root nesting Example: 'foo', 'bar', 'foo/bar/baz', ['foo', 'bar', 'baz'], '../foo/bar'
    • scheme dynamic Scheme marker to associate this meta information with particular mapping scheme
    • converter Declares custom converter instance, to be used for annotated field serialization / deserialization
    • converterParams A Map of parameters to be passed to the converter instance
    • flatten Declares annotated field to be flattened and merged with the host object
    • notNull A bool declares annotated field as NOT NULL for serialization / deserialization process
    • required A bool declares annotated field as required for serialization / deserialization process i.e. needs to be present explicitly
    • inject A bool Declares annotated field value to be directly injected from [DeserializationOptions.injectableValues] during deserialization process
    • ignore A bool declares annotated field as ignored so it will be excluded from serialization / deserialization process
    • ignoreForSerialization A bool declares annotated field as excluded from serialization process
    • ignoreForDeserialization A bool declares annotated field as excluded from deserialization process
    • ignoreIfNull A bool declares annotated field as ignored if it's value is null so it will be excluded from serialization process
    • ignoreIfDefault A bool declares annotated field as ignored if it's value is equals to default so it will be excluded from serialization process
    • defaultValue Defines field default value

Builder

This library introduces own builder used to pre-build Default adapter for your application code. Technically, provided builder wraps the reflectable builder output and adds a bit more generated code to it.

Builder can be configured using build.yaml file at the root of your project.

targets:
  $default:
    builders:
      # This part configures dart_json_mapper builder
      dart_json_mapper:
        options:
          iterables: List, Set, HashSet, UnmodifiableListView
        generate_for:
          - example/**.dart
          - test/_test.dart

      # This part is needed to tell original reflectable builder to stay away
      # it overrides default options for reflectable builder to an **empty** set of files
      reflectable:
        generate_for:
          - no/files

Primary mission for the builder at this point is to generate Iterables support for your custom classes.

Options:

iterables: List, Set, HashSet, UnmodifiableListView

This option if omitted defaults to List, Set is used to configure a list of iterables you would like to be supported for you out of the box. For example you have a Car class in your app and would like to have List and Set support for deserialization, then you could omit this option.

And when you would like to have a deserialization support for other iterables like HashSet , UnmodifiableListView you could add them to the list for this option.

Known limitations

  • Dart code obfuscation. If you are using or planning to use extra-gen-snapshot-options=--obfuscate option with your Flutter project, this library shouldn't be your primary choice then. At the moment there is no workaround for this to play nicely together.

Complementary adapter libraries

If you want a seamless integration with popular use cases, feel free to pick an existing adapter or create one for your use case and make a PR to this repo.

Adapter - is a library which contains a bundle of pre-configured:

For example, you would like to refer to Color type from Flutter in your model class.

  • Make sure you have following dependencies in your pubspec.yaml:

    dependencies:
      dart_json_mapper:
      dart_json_mapper_flutter:
    dev_dependencies:
      build_runner:
  • Usually, adapter library exposes final adapter definition instance, to be provided as a parameter to JsonMapper().useAdapter(adapter)

    import 'dart:ui' show Color;
    import 'package:dart_json_mapper/dart_json_mapper.dart' show JsonMapper, jsonSerializable;    
    import 'package:dart_json_mapper_flutter/dart_json_mapper_flutter.dart' show flutterAdapter;
    
    import 'main.mapper.g.dart' show initializeJsonMapper;
    
    @jsonSerializable
    class ColorfulItem {
      String name;
      Color color;
    
      ColorfulItem(this.name, this.color);
    }
    
    void main() {
      initializeJsonMapper(adapters: [flutterAdapter]);
      
      print(JsonMapper.serialize(
         ColorfulItem('Item 1', Color(0x003f4f5f))
      ));
    }

    output:

    {
      "name": "Item 1",
      "color": "#003F4F5F"
    }

You can easily mix and combine several adapters using following one-liner:

JsonMapper()
   .useAdapter(fixnumAdapter)
   .useAdapter(flutterAdapter)
   .useAdapter(mobXAdapter)
   .useAdapter(builtAdapter)
   .info(); // print out a list of used adapters to console
Comments
  • Any tips on how to use it with multiple model files ?

    Any tips on how to use it with multiple model files ?

    Hi guys,

    It seems to have a bug when trying to use it with the follow structure:

    • lib
      • models
        • model1.dart
        • model2.dart

    I've looked at the generated model1.reflectable.dart and model2.reflectable.dart and the initializeReflectable changes the r.data to the new one so I think this is causing the miss behavior.

    Any tips on that ? I didnt dig deep to see how the internals are working.

    opened by diegomgarcia 22
  • Nullsafety support

    Nullsafety support

    flutter pub get:

    Because every version of flutter_localizations from sdk depends on intl 0.17.0-nullsafety.2 and dart_json_mapper 1.7.2 depends on intl ^0.16.1, flutter_localizations from sdk is incompatible with dart_json_mapper 1.7.2.
    And because no versions of dart_json_mapper match >1.7.2 <2.0.0, flutter_localizations from sdk is incompatible with dart_json_mapper ^1.7.2.
    So, because  depends on both dart_json_mapper ^1.7.2 and flutter_localizations any from sdk, version solving failed.
    
    enhancement 
    opened by aliyazdi75 15
  • Flutter for Web: Dynamic call with too many arguments. Arguments: [Instance of 'NativeJavaScriptObject'

    Flutter for Web: Dynamic call with too many arguments. Arguments: [Instance of 'NativeJavaScriptObject'

    This is a flutter related bug. I've just tried to run my app on Chrome and load in JSON file, but I am encountering an error. It works fine for both Android and iOS.

    This is causing the app to not be able to parse any JSON in the web app. Is there a problem with the way I set this up? Or is it simply the fact the package isn't updated to Flutter 1.20?

    Error Stacktrace

    Error: NoSuchMethodError: ''
    Dynamic call with too many arguments. Expected: 0 Actual: 1
    Receiver: Instance of '({dynamic answer, dynamic markingSteps, dynamic question, dynamic total}) => QuizQuestion'
    Arguments: [Instance of 'NativeJavaScriptObject', question: Instance of 'TextQuestion', answer: Instance of 'TextAnswer', total: 1, markingSteps: 1]
        at Object.throw_ [as throw] (http://localhost:56001/dart_sdk.js:4322:11)
        at Object.defaultNoSuchMethod (http://localhost:56001/dart_sdk.js:4760:15)
        at Function.noSuchMethod (http://localhost:56001/dart_sdk.js:5815:19)
        at Object.noSuchMethod (http://localhost:56001/dart_sdk.js:4756:30)
        at callNSM (http://localhost:56001/dart_sdk.js:4495:19)
        at Object._checkAndCall (http://localhost:56001/dart_sdk.js:4534:12)
        at Object.dcall (http://localhost:56001/dart_sdk.js:4537:17)
        at Function.apply (http://localhost:56001/dart_sdk.js:123788:21)
        at reflectable_builder_based.NonGenericClassMirrorImpl.new.newInstance (http://localhost:56001/packages/reflectable/src/reflectable_base.dart.lib.js:896:28)
        at mapper.JsonMapper._internal.deserializeObject (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:754:108)
        at MappedListIterable.new.<anonymous> (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:711:85)
        at MappedListIterable.new.elementAt (http://localhost:56001/dart_sdk.js:21166:25)
        at ListIterator.new.moveNext (http://localhost:56001/dart_sdk.js:20960:55)
        at JsIterator.next (http://localhost:56001/dart_sdk.js:5989:21)
        at Function.from (http://localhost:56001/dart_sdk.js:43521:20)
        at Function.of (http://localhost:56001/dart_sdk.js:43536:30)
        at MappedListIterable.new.toList (http://localhost:56001/dart_sdk.js:20765:26)
        at mapper.JsonMapper._internal.deserializeIterable (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:711:236)
        at mapper.JsonMapper._internal.deserializeObject (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:725:33)
        at http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:574:22
        at Array.[dartx.forEach] (http://localhost:56001/dart_sdk.js:14691:11)
        at mapper.JsonMapper._internal.enumerateConstructorParameters (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:556:40)
        at mapper.JsonMapper._internal.getNamedArguments (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:595:12)
        at mapper.JsonMapper._internal.deserializeObject (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:753:33)
        at MappedListIterable.new.<anonymous> (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:711:85)
        at MappedListIterable.new.elementAt (http://localhost:56001/dart_sdk.js:21166:25)
        at ListIterator.new.moveNext (http://localhost:56001/dart_sdk.js:20960:55)
        at JsIterator.next (http://localhost:56001/dart_sdk.js:5989:21)
        at Function.from (http://localhost:56001/dart_sdk.js:43521:20)
        at Function.of (http://localhost:56001/dart_sdk.js:43536:30)
        at MappedListIterable.new.toList (http://localhost:56001/dart_sdk.js:20765:26)
        at mapper.JsonMapper._internal.deserializeIterable (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:711:236)
        at mapper.JsonMapper._internal.deserializeObject (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:725:33)
        at http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:574:22
        at Array.[dartx.forEach] (http://localhost:56001/dart_sdk.js:14691:11)
        at mapper.JsonMapper._internal.enumerateConstructorParameters (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:556:40)
        at mapper.JsonMapper._internal.getNamedArguments (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:595:12)
        at mapper.JsonMapper._internal.deserializeObject (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:753:33)
        at Function.deserialize (http://localhost:56001/packages/dart_json_mapper/src/mapper.dart.lib.js:295:59)
        at user_db.InMemoryDb.new.init (http://localhost:56001/packages/qwiz/service/db/user_db.dart.lib.js:86:38)
        at init.next (<anonymous>)
        at http://localhost:56001/dart_sdk.js:37211:33
        at _RootZone.runUnary (http://localhost:56001/dart_sdk.js:37065:58)
        at _FutureListener.thenAwait.handleValue (http://localhost:56001/dart_sdk.js:32049:29)
        at handleValueCallback (http://localhost:56001/dart_sdk.js:32596:49)
        at Function._propagateToListeners (http://localhost:56001/dart_sdk.js:32634:17)
        at _Future.new.[_completeWithValue] (http://localhost:56001/dart_sdk.js:32477:23)
        at async._AsyncCallbackEntry.new.callback (http://localhost:56001/dart_sdk.js:32499:35)
        at Object._microtaskLoop (http://localhost:56001/dart_sdk.js:37326:13)
        at _startMicrotaskLoop (http://localhost:56001/dart_sdk.js:37332:13)
        at http://localhost:56001/dart_sdk.js:32851:9
    
    

    Flutter doctor output

    [✓] Flutter (Channel beta, 1.20.0, on Mac OS X 10.15.5 19F96, locale en-GB)
        • Flutter version 1.20.0 at $HOME/flutter/flutter
        • Framework revision 916c3ac648 (7 days ago), 2020-08-01 09:01:12 -0700
        • Engine revision d6ee1499c2
        • Dart version 2.9.0 (build 2.9.0-21.10.beta)
    
    [✓] Connected device (2 available)
        • Web Server (web) • web-server • web-javascript • Flutter Tools
        • Chrome (web)     • chrome     • web-javascript • Google Chrome 84.0.4147.105
    
    bug release.apk flutter.web 
    opened by petermcneil 13
  • Integration with mobx

    Integration with mobx

    I'm trying to use this excellent library along with dart mobx. Unfortunately I believe that it does not support the self-generated code with mobx_codegen ... For example the simple following class obviously works perfectly:

    @jsonSerializable
    class TestChain {
      List<String> mailingList = List<String>();
    }
    
    main()
    {
      initializeReflectable();
      var test = TestChain();
      test.mailingList.add("[email protected]");
      test.mailingList.add("[email protected]");
      test.mailingList.add("[email protected]");
      var a = JsonMapper.serialize(test); 
      print(a);
      var ab= JsonMapper.deserialize<TestChain>(a);
      print(ab);
    }
    

    If I modify the class so that I can use mobx (even if I don't insert any observable)

    @jsonSerializable
    class TestChain extends _TestChain with _$TestChain {
    
    }
    
    @jsonSerializable
    abstract class _TestChain with Store{
      List<String> mailingList = List<String>();
    }
    

    I get the following error at runtime

    Exception has occurred.
    _TypeError (type 'List<dynamic>' is not a subtype of type 'List<String>' of 'value')
    

    Any suggestions?

    bug good first issue integration 
    opened by felipeleon73 13
  • Can not serialize Map<String, T>

    Can not serialize Map

    Hi is possible to serialize a map of a type? For example:

    @jsonSerializable
    class Person {
       final int age;
       Person({this.age});
    }
    
    Map<String, Person> foo = Map();
    foo['bar'] = Person(age: 30);
    final json = JsonMapper.serialize(foo);
    

    I have been trying this in several ways and I'm not able to get it, but yes I was able to do it with the library json_serializable. I thought this one could do the same stuff with less boilerplate.

    Thanks!

    Edit: I get this error It seems your class '_InternalLinkedHashMap<String, Person>' has not been annotated with @jsonSerializable

    bug good first issue 
    opened by hazzo 12
  • could not generate main.reflectable.dart and need a simple mobx example

    could not generate main.reflectable.dart and need a simple mobx example

    Hello,

    Thanks for the great library,

    I couldn't generate main.reflectable.dart file. I tried to follow the basic setup. I pasted all the additional code in my main.dart file and run the command pub run build_runner build. I get the following error:

    'pub' is not recognized as an internal or external command, operable program or batch file.

    flutter pub run build_runner build command works but it does not generate main.reflectable.dart file.

    Besides, I need a very simple mobx example with a separate store. I am especially interested in very nested json deserialization. This could be used for example json: https://jsonplaceholder.typicode.com/posts

    I already starred the repo. Thank you very much in advance.

    good first issue question 
    opened by deadsoul44 12
  • Api can either return string or object for same field

    Api can either return string or object for same field

    Hello !

    I'm trying to setup dart-json-mapper with a ld+json api.

    API side of serialization could return a string or an object for any relation in my model. See examples below :

    {
      iri: "/packaging_details/1",
      packaging: "/packagings/1",
      name: "bag"
    }
    
    {
      iri: "/packaging_details/1",
      packaging: {iri: "/packagings/1", weight: 10},
      name: "bag"
    }
    

    When I have a string, it ends with the following error :

    Unhandled Exception: It seems your Enum class field is missing annotation:
    @JsonProperty(enumValues: Packaging.values)
    

    I think I should do a converter but I don't really understand how to implement it. Also, should I make a converter for each model or it's possible to make a "universal" one ?

    Thank you

    question 
    opened by yobud 10
  • The method 'map' was called on null.

    The method 'map' was called on null.

    Exception has occurred.
    NoSuchMethodError (NoSuchMethodError: The method 'map' was called on null.
    Receiver: null
    Tried calling: map<Object>(Closure: (dynamic) => Object))
    

    I am using :

    @jsonSerializable
    class UserModel {
      String fullName;
      String password;
      int age = 25;
      int gender = 1;
      bool isActive;
      AccountModel accountBank;
      int id;
      String userName;
      String email;
      String avatar;
      List<ContactModel> contacts;
    
      UserModel({
        this.fullName,
        this.password,
        this.accountBank,
        this.age,
        this.email,
        this.gender,
        this.id,
        this.userName,
        this.avatar,
        this.isActive,
        this.contacts,
      });
    }
    

    and

    @jsonSerializable
    class ContactModel {
      String value;
      @JsonProperty(name: "contact_type")
      ContactTypeModel contactType;
      UserModel user;
      @JsonProperty(name: "contact_type_id")
      int contactTypeId;
      int id;
    
      ContactModel({
        this.value,
        this.contactType,
        this.user,
        this.contactTypeId,
        this.id,
      });
    }
    

    if I delete List<ContactModel> contacts property , I don't get this issue. but in my case , I shouldn't delete that. also in my main :

    initializeReflectable();
      JsonMapper().useAdapter(
        JsonMapperAdapter(
          valueDecorators: {
            typeOf<List<ContactModel>>(): (value) => value.cast<ContactModel>()
          },
        ),
      );
      runApp(child: MyApp());
    
    bug good first issue 
    opened by mohammadne 10
  • Bug in tests: '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Pt'

    Bug in tests: '_InternalLinkedHashMap' is not a subtype of type 'Pt'

    Hi! I got an error with deserializing so i decided test it inside your test themselves. I added deserializing inside test test/test.constructors.dart:170 in this way:

        String pTargetJson = JsonMapper.serialize(pTarget, '');
        final PtDerived pTargetBack = JsonMapper.deserialize(pTargetJson); // My string
    

    and got an error too type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'Pt'

    bug 
    opened by ValeriusGC 10
  • Problem converting enum with following uppercase character

    Problem converting enum with following uppercase character

    I have the following classes:

    @JsonSerializable()
    @Json(caseStyle: CaseStyle.pascal)
    class TestModel {
      String? id;
      EDeviceRadioType radio = EDeviceRadioType.LoRa;
    }
    
    @JsonSerializable()
    class TestParent {
      TestModel? newDevice;
    }
    

    And this enum:

    @JsonSerializable()
    enum EDeviceRadioType { LoRa, NBIoT, MBus, MobileInternet, WMBus }
    

    This deserialization works:

    TestModel model = TestModel();
    model.id = '123';
    model.radio = EDeviceRadioType.WMBus;
    var serialized = JsonMapper.serialize(parent);
    var deserialized = JsonMapper.deserialize<TestModel>(serialized);
    

    But this does not work:

    TestParent parent = TestParent();
    TestModel model = TestModel();
    model.id = '123';
    model.radio = EDeviceRadioType.WMBus;
    parent.newDevice = model;
    
    var serialized = JsonMapper.serialize(parent);
    var deserialized = JsonMapper.deserialize<TestParent>(serialized);
    

    The deserialize function fails with:_TypeError (type 'Null' is not a subtype of type 'EDeviceRadioType' of 'value')

    But if I change the enum to a type which does not have a following upper case character, the deserialization works:

    TestParent parent = TestParent();
    TestModel model = TestModel();
    model.id = '123';
    model.radio = EDeviceRadioType.LoRa;
    parent.newDevice = model;
    
    var serialized = JsonMapper.serialize(parent);
    var deserialized = JsonMapper.deserialize<TestParent>(serialized);
    

    As far as I can read, WMBus and alike should be valid PascalCase. But nonetheless, it is weird that it works if the model is not wrapped in a parent. Am I missing anything here? 🤔

    bug 
    opened by fasterlars 9
  • Support for String/Num Enum values

    Support for String/Num Enum values

    Enum needs to be annotated what value we see in code and what name of the value is in JSON. eg

    • enum Color {Red, Blue} I need to be able to use Color.Red in Dart, but serialise Red to red In real life example, (zyx) or _abc are valid enum values that can be present in JSON
    • Smiliarly for numeric enums 1.1 and -2 are valid values Value in code is something we generated in openapi, eg. in spec you see -1.1 we should generate minusOneDotOne or something similar, but we need a way to tell JSON serialiser to turn minusOneDotOne into -1.1 in JSON and it has to be -1.1 double value not string value "-1.1"
    enhancement 
    opened by agilob 9
  • Introduce converter for `MaterialColor` class in Flutter adapter

    Introduce converter for `MaterialColor` class in Flutter adapter

    E/flutter (24684): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: It seems your class 'MaterialColor' has not been annotated with @jsonSerializable
    

    i'm coming from the json_serializable package where these errors would instead occur when running build_runner

    enhancement sponsorware adapter 
    opened by DetachHead 2
  • Introduce `raw` attribute to `@JsonProperty` meta

    Introduce `raw` attribute to `@JsonProperty` meta

    @jsonSerializable
    class RawBean {
        String? name;
    
        @JsonProperty(raw: true)
        String? json;
    
        RawBean(this.name, this.json);
    }
    
    final bean = RawBean('My bean', '{"attr":false}');
    
    

    should produce:

    {
        "name":"My bean",
        "json":{
            "attr":false
        }
    }
    

    inspired by this

    enhancement sponsorware 
    opened by k-paxian 0
  • Introduce linting rules covering annotations configuration validation

    Introduce linting rules covering annotations configuration validation

    Currently, when trying to call JsonMapper.fromJson<SomeObject>(object) it may throw a runtime exception like:

    Unhandled Exception: It seems your class 'SomeObject' has not been annotated with @jsonSerializable
    

    This can happen for multiple reasons (forgot @jsonSerializable, some missing type adapter, or by forgetting to run the code generation). From my experience with using this package, it can be pretty difficult to debug this issue as there is no help from the compiler.

    My proposition is to add a code generator that generates type-safe code for (de)serialization. This could even be in the form of an extension function on JsonMapper like:

    extension on JsonMapper {
      SomeObject deserializeSomeObject(String json) {...}
    }
    

    As this package is using reflectable, we'd need to write a new code generator that writes this class. This class could be purely optional and just a compile-safe addition to the package.

    If this is something that people find interesting and you'd like to see implemented. I'd be happy to help out/ write a PR.

    enhancement question sponsorware 
    opened by Norbert515 4
  • Can`t use JsonMapper in production build

    Can`t use JsonMapper in production build

    Given this code

    import 'package:dart_json_mapper/json_mapper.dart';
    import 'package:dart_json_mapper/annotations.dart';
    
    import 'main.reflectable.dart';
    
    @jsonSerializable
    class TridaA {
      TridaA() {
        print("TridaA constructed");
      }
      String prop;
    }
    
    void main() async {
      // inicializace reflektoru
      initializeReflectable();
    
      print(".main");
      jsonSerializable.annotatedClasses.forEach((cls) {
        print("Annotated class ${cls.qualifiedName}");
      });
    
      print("allClasses ${jsonSerializable.annotatedClasses}");
      var mirror = jsonSerializable.annotatedClasses.firstWhere((cm) {
        print("   class ${cm.simpleName} ~ ${cm.reflectedType.toString()}");
        return cm.simpleName == "TridaA";
      });
    
      print("Instance by reflection: ${mirror.newInstance("", [], {})}");
    
      var map = Map<String, dynamic>.from({"b": Map<String, dynamic>.from({"prop": "test"})});
    
      var instance = JsonMapper.instance
        .deserializeObject(map, TridaA);
    
      print("Instance by JsonMapper: ${instance}");
    
    }
    

    Everything is OK if it is built with dartdevc, but when compiled with dart2js by: webdev serve web:43751 --hot-reload --release with minification, it raise error:

    Uncaught It seems your class 'minified:a6' has not been annotated with @jsonSerializable at Object.c (localhost:43751/main.dart.js:2719:3) at bX.a2 (localhost:43751/main.dart.js:5437:20) at bX.bB (localhost:43751/main.dart.js:5442:30) at localhost:43751/main.dart.js:6122:49 at ef.a (localhost:43751/main.dart.js:3678:72) at ef.$2 (localhost:43751/main.dart.js:3804:23) at Object.jI (localhost:43751/main.dart.js:3659:3) at Object.fD (localhost:43751/main.dart.js:6124:10) at localhost:43751/main.dart.js:6532:8 at localhost:43751/main.dart.js:6527:55

    bug flutter.web 
    opened by fidlip 4
Owner
Alexander Mazuruk
"And where is `home'?" "K-PAX." "Kaypacks?" "Kay-hyphen-pee-ay-ex. K-PAX."
Alexander Mazuruk
An auto mapper for Dart. It allows mapping objects of different classes automatically and manually using JSON serialization.

AutoMapper for Dart An auto mapper for Dart. It allows mapping objects of different classes automatically and manually using JSON serialization. Examp

Leynier Gutiérrez González 7 Aug 24, 2022
Extension functions on ValueListenable that allows you to work with them almost as if it was a synchronous stream.

functional_listener Extension functions on ValueListenable that allows you to work with them almost as if it was a synchronous stream. Each extension

null 54 Oct 9, 2022
A flutter package that allows you to transform your excel to json

excel_to_json A package that allows you to transform your excel to the following format: Excel To JSON Getting Started At current the package allows y

Vitor Amaral de Melo 0 Nov 7, 2022
Provides null-safety implementation to simplify JSON data handling by adding extension method to JSON object

Lazy JSON Provides null-safety implementation to simplify JSON data handling by adding extension method to JSON object and JSON array. Getting started

Kinnara Digital Studio 0 Oct 27, 2021
A simple Flutter / Dart Utility class for converting complex objects to uri and query string

A simple Flutter / Dart Utility class for converting complex or nested objects to uri and query strings you can follow the the article on how this cla

Opata Joshua 5 Sep 7, 2022
This package binds to Cronet's native API to expose them in Dart.

Experimental Cronet Dart bindings This package binds to Cronet's native API to expose them in Dart. This is an HTTP Client Package with almost the sam

Google 103 Dec 9, 2022
A simple shortcut, command line interface (CLI) for a lazy (a.k.a effective) Flutter developer in order to increase productivity and happiness.

f A simple shortcut, command line interface (CLI) for a lazy (a.k.a effective) Flutter developer in order to increase productivity and happiness. Inst

Salman S 27 Nov 22, 2022
A few handy Flutter tools, dead simple `UriRouter` for `Uri`-based navigator or `BuildTracker` to track widget rebuilds and what caused them to rebuild.

noob A few handy tools for Flutter apps. UriRouter Hooks Providers PointerIndicator BuildTracker PeriodicListenable UriRouter Dead simple Uri-based pa

null 6 Jan 18, 2022
A CLI tool to help generate dart classes from json returned from API

Json 2 Dart Command line utility Important note There is already a package called json2dart so this package will be called json2dartc ! This project w

Adib Mohsin 38 Oct 5, 2022
Cache json map to local file with Dart:io. Read file with sync api.

local_cache_sync 一个非常简单易用的Flutter本地储存库,适用于在本地储存一列轻量数据(例如用户保存在本地的设备信息,或者缓存一系列用户信息)。 local_cache_sync的所有方法都是同步,而不是异步的。这意味着你不需要使用await就可以获取数据。在flutter中,这

null 16 Jun 24, 2022
From JSON to Dart Advanced

From JSON to Dart Advanced Table of Contents Features Convert from clipboard Convert from selection Convert from clipboard to code generation Convert

 нιяαитнα 80 Dec 17, 2022
Args simple - A simple argument parser and handler, integrated with JSON and dart

args_simple A simple argument parser and handler, integrated with JSON and dart:

Graciliano Monteiro Passos 1 Jan 22, 2022
A flutter package allows you to search all universities in Brazil

Universities This package allows you to search all universities in Brazil. Usage To use this plugin, add universities as a dependency in your pubspec.

Gabriel Patrick Souza 2 Oct 24, 2021
A generator to create config class from json files that support many environments

A generator to create config class from json files that support many environments. Motivation If you use a json file to config your applications, perp

Diego Cardenas 0 Oct 9, 2021
JSON API parser for Flutter

Flutter Japx - JSON:API Decoder/Encoder Lightweight [JSON:API][1] parser that flattens complex [JSON:API][1] structure and turns it into simple JSON a

Infinum 23 Dec 20, 2022
library to help you create database on local memory, support json local database inspired by lowdb

Licensed Licensed under the MIT License <http://opensource.org/licenses/MIT>. SPDX-License-Identifier: MIT Copyright (c) 2021 Azkadev <http://github.c

Azka Full Snack Developer:) 35 Oct 17, 2022
A flutter application , that create dynamic forms from json data

checktyper A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if th

DotCoder 1 Aug 23, 2022
Generates utilities to aid in serializing to/from JSON.

Provides Dart Build System builders for handling JSON. json_serializable Package: https://pub.dev/packages/json_serializable Source code The core pack

Google 1.4k Jan 8, 2023
A Dart package which supports checking if a current package is up-to-date.

pub_updater A Dart package which enables checking whether packages are up to date and supports updating them. Intended for use in CLIs for prompting u

Very Good Open Source 47 Oct 27, 2022