Giter Site home page Giter Site logo

k-paxian / dart-json-mapper Goto Github PK

View Code? Open in Web Editor NEW
399.0 6.0 33.0 2.07 MB

Serialize / Deserialize Dart Objects to / from JSON

Home Page: https://pub.dev/packages/dart_json_mapper

License: Other

Dart 100.00%
flutter dart json serialization deserialization dartlang dart-json-mapper reflection tojson fromjson

dart-json-mapper's Introduction

SWUbanner Build Status pub package Pub Points Popularity

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.

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

@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<dynamic> type which can't be directly set to the concrete target field List<Car> for instance. So obvious workaround will be to cast List<dynamic> => List<Car>, which can be performed as List<dynamic>().cast<Car>().

Basic iterable based generics using Dart built-in types like List<num>, List<String>, List<bool>, List<DateTime>, Set<num>, Set<String>, Set<bool>, Set<DateTime>, 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

    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<Car> / Set<Car> we don't have to provide value decorators as showed in a code snippet below, thanks to the Builder

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<Car> / UnmodifiableListView<Car> 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.

@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.

@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: <your property value>) 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.

@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

@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<String, dynamic> 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<K, V> type then you can pass typed instance of it as a template parameter for DeserializationOptions.

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

// 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).

@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<K, V>, 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<Car> and Set<Car> support for deserialization, then you could omit this option.

And when you would like to have a deserialization support for other iterables like HashSet<Car>, UnmodifiableListView<Car> 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

dart-json-mapper's People

Contributors

herrniklasraab avatar ipcjs avatar k-paxian avatar kennelken avatar norbert515 avatar schultek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dart-json-mapper's Issues

Enum and Equatable incompatibility

Hi,

Run into this issue an issue with equatable today.

The following code :

import 'package:dart_json_mapper/dart_json_mapper.dart';
import 'package:equatable/equatable.dart';

enum MyColor { black, red }

@jsonSerializable
@Json(ignoreNullMembers: true)
class MyCarModel extends Equatable {
  final String model;

  @JsonProperty(enumValues: MyColor.values)
  final MyColor color;

  const MyCarModel({this.model, this.color});

  @override
  List<Object> get props => [model, color];

  Map<String, dynamic> toJson() {
    return JsonMapper.toMap(this);
  }
}

Will trigger this exception :

--¶ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ¶----------------------------------------------------
The following _MissingEnumValuesErrorImpl was thrown running a test:
It seems your Enum class field is missing annotation:
@JsonProperty(enumValues: MyColor.values)

When the exception was thrown, this was the stack:
#0      JsonMapper.serializeObject (package:dart_json_mapper/src/mapper.dart:481:9)
#1      JsonMapper.serializeIterable.<anonymous closure> (package:dart_json_mapper/src/mapper.dart:452:32)
#2      MappedListIterable.elementAt (dart:_internal/iterable.dart:417:29)
#3      ListIterable.toList (dart:_internal/iterable.dart:221:19)
#4      JsonMapper.serializeIterable (package:dart_json_mapper/src/mapper.dart:452:64)
#5      JsonMapper.serializeObject.<anonymous closure> (package:dart_json_mapper/src/mapper.dart:529:32)
#6      JsonMapper.enumeratePublicFields (package:dart_json_mapper/src/mapper.dart:329:14)
#7      JsonMapper.serializeObject (package:dart_json_mapper/src/mapper.dart:509:5)
#8      JsonMapper.serialize (package:dart_json_mapper/src/mapper.dart:40:18)
#9      JsonMapper.toMap (package:dart_json_mapper/src/mapper.dart:60:9)
#10     MyCarModel.toJson (package:easivio/domain/models/user_model.dart:168:23)
#11     main.<anonymous closure>.<anonymous closure> (file:///C:/Users/Stephane/easivio/easivio-mobile/test/onboarding_test.dart:47:29)
<asynchronous suspension>
#12     testWidgets.<anonymous closure>.<anonymous closure> (package:flutter_test/src/widget_tester.dart:124:25)
#13     TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:696:19)
<asynchronous suspension>
#16     TestWidgetsFlutterBinding._runTest (package:flutter_test/src/binding.dart:679:14)
#17     AutomatedTestWidgetsFlutterBinding.runTest.<anonymous closure> (package:flutter_test/src/binding.dart:1050:24)
#23     AutomatedTestWidgetsFlutterBinding.runTest (package:flutter_test/src/binding.dart:1047:15)
#24     testWidgets.<anonymous closure> (package:flutter_test/src/widget_tester.dart:121:22)
#25     Declarer.test.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:171:27)
<asynchronous suspension>
#26     Invoker.waitForOutstandingCallbacks.<anonymous closure> (package:test_api/src/backend/invoker.dart:242:15)
#31     Invoker.waitForOutstandingCallbacks (package:test_api/src/backend/invoker.dart:239:5)
#32     Declarer.test.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/declarer.dart:169:33)
#37     Declarer.test.<anonymous closure> (package:test_api/src/backend/declarer.dart:168:13)
#38     Invoker._onRun.<anonymous closure>.<anonymous closure>.<anonymous closure>.<anonymous closure> (package:test_api/src/backend/invoker.dart:392:25)
#52     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:384:19)
#53     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:418:5)
#54     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:174:12)
(elided 28 frames from class _FakeAsync, package dart:async, package dart:async-patch, and package stack_trace)

Regards

Serialization issue with 2 of the same object

Hi,

User user = User();
    Device d = Device(uuid: Uuid());
    user.devices = [];
    user.devices.add(d);
    user.devices.add(d);
    print(JsonMapper.serialize(user));

This genrerate a serialization error :

`E/flutter (31538): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: NoSuchMethodError: The method '&' was called on null.
E/flutter (31538): Receiver: null
E/flutter (31538): Tried calling: &(4294967295)
E/flutter (31538): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
E/flutter (31538): #1 new Color (dart:ui/painting.dart:107:42)
E/flutter (31538): #2 WidgetConfigurationModel.backgroundColor/…/models/widget_configuration_model.dart:23
E/flutter (31538): #3 _data./main.reflectable.dart:3587
E/flutter (31538): #4 _InstanceMirrorImpl.invokeGetter
package:reflectable/src/reflectable_builder_based.dart:340
E/flutter (31538): #5 JsonMapper.enumeratePublicFields
package:dart_json_mapper/src/mapper.dart:287
E/flutter (31538): #6 JsonMapper.serializeObject
package:dart_json_mapper/src/mapper.dart:453
E/flutter (31538): #7 JsonMapper.serializeObject.
package:dart_json_mapper/src/mapper.dart:464
E/flutter (31538): #8 JsonMapper.enumeratePublicFields
package:dart_json_mapper/src/mapper.dart:298
E/flutter (31538): #9 JsonMapper.serializeObject
package:dart_json_mapper/src/mapper.dart:453
E/flutter (31538): #10 JsonMapper.serializeObject.
package:dart_json_mapper/src/mapper.dart:420
E/flutter (31538): #11 MappedListIterable.elementAt (dart:_internal/iterable.dart:417:29)
E/flutter (31538): #12 ListIterable.toList (dart:_internal/iterable.dart:221:19)
E/flutter (31538): #13 JsonMapper.serializeObject
package:dart_json_mapper/src/mapper.dart:420
E/flutter (31538): #14 JsonMapper.serializeObject.
package:dart_json_mapper/src/mapper.dart:464
E/flutter (31538): #15 JsonMapper.enumeratePublicFields
package:dart_json_mapper/src/mapper.dart:298
E/flutter (31538): #16 JsonMapper.serializeObject
package:dart_json_mapper/src/mapper.dart:453
E/flutter (31538): #17 JsonMapper.serializeObject.
package:dart_json_mapper/src/mapper.dart:464
E/flutter (31538): #18 JsonMapper.enumeratePublicFields
package:dart_json_mapper/src/mapper.dart:298
E/flutter (31538): #19 JsonMapper.serializeObject
package:dart_json_mapper/src/mapper.dart:453
E/flutter (31538): #20 JsonMapper.serializeObject.
package:dart_json_mapper/src/mapper.dart:420
E/flutter (31538): #21 MappedListIterable.elementAt (dart:_internal/iterable.dart:417:29)
E/flutter (31538): #22 ListIterable.toList (dart:_internal/iterable.dart:221:19)
E/flutter (31538): #23 JsonMapper.serializeObject
package:dart_json_mapper/src/mapper.dart:420
E/flutter (31538): #24 JsonMapper.serializeObject.
package:dart_json_mapper/src/mapper.dart:464
E/flutter (31538): #25 JsonMapper.enumeratePublicFields
package:dart_json_mapper/src/mapper.dart:298
E/flutter (31538): #26 JsonMapper.serializeObject
package:dart_json_mapper/src/mapper.dart:453
E/flutter (31538): #27 JsonMapper.serialize
package:dart_json_mapper/src/mapper.dart:63
E/flutter (31538): #28 User.toJson/…/models/user_model.dart:72
E/flutter (31538): #29 UserRepositories.updateUser/…/repositories/user_repositories.dart:60
E/flutter (31538): #30 UserService.updateUser/…/services/user_service.dart:170
E/flutter (31538): #31 UserService.broadcastPlayListOnDevice/…/services/user_service.dart:166
E/flutter (31538): #32 _DevicePlayListViewState.selectPlayList/…/playlist/device_playlist_view.dart:44
E/flutter (31538): #33 _PlayListBottomBarState.selectPlayList/…/widgets/playlist_bottom_bar.dart:25
E/flutter (31538): #34 _PlaylistSelectionCardState.build./…/widgets/playlist_selection_card.dart:30
E/flutter (31538): #35 _InkResponseState._handleTap
package:flutter/…/material/ink_well.dart:706
E/flutter (31538): #36 _InkResponseState.build.
package:flutter/…/material/ink_well.dart:789
E/flutter (31538): #37 GestureRecognizer.invokeCallback
package:flutter/…/gestures/recognizer.dart:182
E/flutter (31538): #38 TapGestureRecognizer.handleTapUp
package:flutter/…/gestures/tap.dart:486
E/flutter (31538): #39 BaseTapGestureRecognizer._checkUp
package:flutter/…/gestures/tap.dart:264
E/flutter (31538): #40 BaseTapGestureRecognizer.acceptGesture
package:flutter/…/gestures/tap.dart:236
E/flutter (31538): #41 GestureArenaManager.sweep
package:flutter/…/gestures/arena.dart:156
E/flutter (31538): #42 GestureBinding.handleEvent (package:flutter/src `

Let me know if you need more information.

Is this library may work with inheritance and generics?

Hi!
The fact is i tried to work with

@jsonSerializable
class Base<T> {
  final T value;
  Base(this.value);
}

@jsonSerializable
class Derived extends Base<String>{
  Derived(String value) : super(value);
}

and just simply with

@jsonSerializable
class Base {
  final String value;

  Base(this.value);
}

@jsonSerializable
class Derived extends Base{
  Derived(String value) : super(value);
}

and got the same errors:

NoSuchMethodError: The getter 'reflectedType' was called on null.
  Receiver: null
  Tried calling: reflectedType
  dart:core/runtime/libobject_patch.dart 50:5        Object.noSuchMethod
  package:dart_json_mapper/json_mapper.dart 228:62   JsonMapper.enumeratePublicFields
  package:dart_json_mapper/json_mapper.dart 355:5    JsonMapper.serializeObject
  package:dart_json_mapper/json_mapper.dart 45:37    JsonMapper.serialize
  bin/test/undostacktest.dart 33:22                  main.<fn>
  package:test_api/src/backend/declarer.dart 168:27  Declarer.test.<fn>.<fn>.<fn>

my .yaml is like

dependencies:
  json_annotation: ^1.0.0
dev_dependencies:
  build_runner: ^0.10.1+1
  dart_json_mapper: ^1.0.8
  test: ^1.5.1+1

using deep nested collections at dart_json_mapper_mobx

I can't guess what I'm doing wrong, here is my json

[
  {
    "title": "Salads & Dips",
    "items": [
      {
        "id": "1",
        "title": "Columbia Salad",
      }
    ]
  }
]

and mobx stores

@jsonSerializable
class MenuSectionStore extends _MenuSectionStore with _$MenuSectionStore {

}

abstract class _MenuSectionStore with Store {
  @observable
  String title = '';

  @observable
  @JsonProperty(ignoreIfNull: true)
  ObservableList<MenuItemStore> items = ObservableList<MenuItemStore>();
}


@jsonSerializable
class MenuItemStore extends _MenuItemStore with _$MenuItemStore {

}

abstract class _MenuItemStore with Store {
  @observable
  String id;

  @observable
  String title = '';
}

JsonMapper().useAdapter(
      mobXAdapter
    );

// this string is works
JsonMapper.deserialize<MenuSectionStore>(jsonResponse[0]);
// this cause error
JsonMapper.deserialize<ObservableList<MenuSectionStore>>(jsonResponse);

errors is

[VERBOSE-2:ui_dart_state.cc(157)] Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'MenuItemStore' of 'element' #0 ObservableList.add (package:mobx/src/api/observable_collections/observable_list.dart) #1 IterableConverter.fromJSON.<anonymous closure> (package:dart_json_mapper_mobx/dart_json_mapper_mobx.dart:181:57) #2 List.forEach (dart:core-patch/growable_array.dart:282:8) #3 IterableConverter.fromJSON (package:dart_json_mapper_mobx/dart_json_mapper_mobx.dart:181:19) #4 JsonMapper.deserializeObject (package:dart_json_mapper/src/mapper.dart:549:38) #5 JsonMapper.deserialize (package:dart_json_mapper/src/mapper.dart:46:21)

Issue with Generic type ?

Hi,

I run into an issue :

image

Unsupported operation: Attempt to obtain reflectedType from generic class .EasivioModel.

This append also to a non-abstract class.

import 'dart:convert';

import 'package:dart_json_mapper/dart_json_mapper.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';

@immutable
@jsonSerializable
@Json(typeNameProperty: 'technicalName', ignoreNullMembers: true)
abstract class EasivioModel<T> extends Equatable {
  final String parentUuid;
  final String uuid;

  const EasivioModel({this.parentUuid, this.uuid});

  @override
  List<Object> get props => [
        parentUuid,
        uuid,
      ];

  @override
  bool get stringify => true;

  T copyWith();

  T merge(T other);

  T fromJson(Map<String, dynamic> jsonData) {
    return JsonMapper.deserialize<T>(json.encode(jsonData));
  }

  Map<String, dynamic> toJson() {
    return json.decode(JsonMapper.serialize(this));
  }
}

Commenting the @jsonSerializable resolve the issue but I need it to comply with :
First thing you should do is to put @jsonSerializable annotation on each of those classes
=> It seems your class 'EasivioModel<dynamic>' has not been annotated with @jsonSerializable

Something I do wrong ?

A few Questions

Hello @k-paxian,

https://codingpassion.net just joined as Encouragement Sponsor! We love the sponsorware approach. This decision was made because we want to commit longterm to this framework. And we will have a lot of questions and requirements in the future. So thank you for that!

A few questions/requirements that came up while developing:

  • Is there any way to get support for enums in Lists? For example List<Color>()
  • Would it be possible to specify @Json(typeNameProperty: 'typeName') in SerializationOptions() globally?
  • Would it be possible to have an option in SerializationOptions(), that would serialize/deserialize only properties marked with @JsonProperty() and ignores all unmarked?
  • Waiting for #38 (HashSet, UnmodifiableListView, UnmodifiableMapView are needed)
  • Do sponsors get features marked with "sponsorware" immediately or earlier?

Thanks,

Niklas

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());

Failed when try to work with complex objects (not String, num, etc)

When i try to serialize something more complex then primitive i got error like that

Converting object to an encodable object failed: Instance of 'Pt'

my test code:

@jsonSerializable
class Base<T> {
  final T value;
  Base(this.value);
}

@jsonSerializable
class Pt {
  Pt();
}

@jsonSerializable
class PtDerived extends Base<Pt>{
  PtDerived(Pt value) : super(value);
}

void main() {
  initializeReflectable(); // Imported from main.reflectable.dart
    var pt = Pt();
    print(JsonMapper.serialize(pt));
    var s2 = JsonMapper.serialize(PtDerived(pt));
    print(s2);
...

How to use ObservableList with json from api

import 'package:mobx/mobx.dart';
import 'package:dart_json_mapper/dart_json_mapper.dart';
part 'test_dart_json_map.g.dart';

@jsonSerializable
class TestItem with Store{
  int id;
 
  @observable
  String content;

  TestItem({this.id, this.content});
}

@jsonSerializable
class TestChain = _TestChain with _$TestChain;

@jsonSerializable
abstract class _TestChain with Store {
  final json = [ // from api response
    {
      'id': 1,
      'content': '1'
    },
    {
      'id': 2,
      'content': '2'
    },
  ];

  ObservableList<TestItem> mailingList = ObservableList<TestItem>();
  _TestChain(this.mailingList);
  final result = JsonMapper.serialize(
    TestChain(ObservableList<TestItem>.of(json)) // there is an error
  );

  @action 
  addItem(TestItem item) {
    mailingList.add(item);
  }
  @action 
  changeItem() {
    mailingList[0].content = 'change';
  }
}

expect:
i want use json(from api) to convert ObservableList
error:
type 'ObservableList<String>' is not a subtype of type 'ObservableList<TestItem>' of 'function

Can't convert List<Car>

// Deserialize
print(JsonMapper.deserialize<Person>(personJson));

print to:

Unhandled exception:
type List<dynamic> is not a subtype of type List<Car> of 'value'
#0      _data.<anonymous closure> (file:///E:/Flutter/dart-json-mapper-master/example/example.reflectable.dart:519:61)
#1      _InstanceMirrorImpl.invokeSetter (package:reflectable/src/reflectable_builder_based.dart:346:20)
#2      JsonMapper.deserializeObject.<anonymous closure> (package:dart_json_mapper/json_mapper.dart:523:12)
#3      JsonMapper.enumeratePublicFields (package:dart_json_mapper/json_mapper.dart:296:14)
#4      JsonMapper.deserializeObject (package:dart_json_mapper/json_mapper.dart:502:5)
#5      JsonMapper.deserialize (package:dart_json_mapper/json_mapper.dart:59:21)
#6      main (file:///E:/Flutter/dart-json-mapper-master/example/example.dart:101:20)
#7      _startIsolate.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:301:19)
#8      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:172:12)

Bug in tests: '_InternalLinkedHashMap<String, dynamic>' 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'

[Question] Compatibility with Freezed ?

All is in the title ;)

Is there a way to define the default serializer to make it compatible with Freezed ?
Since it support decorator, that should be feasible, what do you think ?

Regards

Ignore Unknown Types during serialization

Hi @k-paxian,

I was afraid you got bored, so I found you a new issue 😂

My Map (Firebase) has a DateTime field but it's not present on my Model, but JsonMapper still want to deserialize it :
JsonUnsupportedObjectError (Converting object to an encodable object failed: Instance of 'Timestamp')

Was able to reproduce it with this exemple :

import 'package:dart_json_mapper/dart_json_mapper.dart';

enum MyColor { black, red }

@jsonSerializable
class MyCarModel {
  const MyCarModel({this.model});

  final String model;

  Map<String, dynamic> toJson() {
    return JsonMapper.toMap(this);
  }
}
// given
final Map<String, dynamic> json = <String, dynamic>{
   'model': 'Tesla',
   'DateFacturation': Timestamp(1568465485, 0),
};

// when
final MyCarModel myModel = JsonMapper.fromMap(json);

// then
expect(myModel.model, 'Tesla');

Regards

Ability to have “partial” deserialization from JSON

In many cases input JSON has many fields, which are not needed for particular UI. So I’m declaring class with let’s say 5 fields out of 20 in JSON. Currently I’m getting error on attempt to deserialize from JSON in such situations, so I have to carefully declare all fields in my classes to be fully consistent with input JSON, which is boring and useless in many cases.

Failed serializing enum on extended class

Given this sample:

import 'package:dart_json_mapper/annotations.dart';
import 'package:dart_json_mapper/json_mapper.dart';

import 'main.reflectable.dart';

enum OperationType { insert, update, remove }


@jsonSerializable
class Change {
  String path;
  @JsonProperty(enumValues: OperationType.values)
  OperationType operation;
  Change({this.path, this.operation});
}

@jsonSerializable
class ContentChange extends Change {
  Map<String, dynamic> value;
  ContentChange({String path, OperationType operation, this.value})
      : super(path: path, operation: operation);
}

@jsonSerializable
class UpdateChange extends ContentChange {
  UpdateChange({String path, Map<String, dynamic> item})
      : super(path: path, operation: OperationType.update, value: item);
}

void main() {
  initializeReflectable();

  var change = Change(operation: OperationType.update, path: "info");
  var contentChange = ContentChange(operation: OperationType.update, path: "info", value: {"name": "newName"});
  var updateChange = UpdateChange(path: "info", item: {"name": "newName"});

  // this is OK
  var serialized = JsonMapper.serialize(change);
  print("serialized change:$serialized");

  // this will throw error!!!
  serialized = JsonMapper.serialize(contentChange);
  print("serialized contentChange:$serialized");

  // also error
  serialized = JsonMapper.serialize(updateChange);
  print("serialized updateChange:$serialized");
}

This will print:

> serialized change:{
>  "path": "info",
>  "operation": "OperationType.update"
> }
> Unhandled exception:
> It seems your Enum class field is missing annotation:
> @JsonProperty(enumValues: OperationType.values)
> #0      JsonMapper.serializeObject (package:dart_json_mapper/json_mapper.dart:448:9)
> #1      JsonMapper.serializeObject.<anonymous closure> (package:dart_json_mapper/json_mapper.dart:467:28)
> #2      JsonMapper.enumeratePublicFields (package:dart_json_mapper/json_mapper.dart:303:14)
> #3      JsonMapper.serializeObject (package:dart_json_mapper/json_mapper.dart:456:5)
> #4      JsonMapper.serialize (package:dart_json_mapper/json_mapper.dart:48:37)
> #5      main (file:///home/fidlip/dev/wspace/json_mapper_test/bin/main.dart:53:27)
> #6      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
> #7      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
> 
> 

Is it intended to support classes with enums inherited from parent class?

Tested on:
v. 1.6
SDK 2.1.0

Global Settings

Hello,

is there a way to for example to set ignoreNullMembersto true for the whole project? Or was it a design decision to don't have a global configuration?

Best,

Niklas

Can't deserialize when value of nested object is null.

There is classes like this:

@jsonSerializable
class Foo {
  final Bar bar;
  final String message;

  Foo(this.bar, this.message);
}

@jsonSerializable
class Bar {
  final Baz baz;

  Bar(this.baz);
}

@jsonSerializable
class Baz {}

and json:

{ "message": "hello world" }
// { "message": "hello world", "bar": null } also

I get the following error:

NoSuchMethodError: The method 'containsKey' was called on null.
Receiver: null
Tried calling: containsKey("baz")

dart:core                                         Object.noSuchMethod
package:dart_json_mapper/json_mapper.dart 355:19  JsonMapper.getPositionalArguments.<fn>
package:dart_json_mapper/json_mapper.dart 316:14  JsonMapper.enumerateConstructorParameters.<fn>
dart:core                                         List.forEach
package:dart_json_mapper/json_mapper.dart 302:29  JsonMapper.enumerateConstructorParameters
package:dart_json_mapper/json_mapper.dart 351:5   JsonMapper.getPositionalArguments
package:dart_json_mapper/json_mapper.dart 447:30  JsonMapper.deserializeObject
package:dart_json_mapper/json_mapper.dart 459:22  JsonMapper.deserializeObject.<fn>
package:dart_json_mapper/json_mapper.dart 285:14  JsonMapper.enumeratePublicFields
package:dart_json_mapper/json_mapper.dart 451:5   JsonMapper.deserializeObject
package:dart_json_mapper/json_mapper.dart 51:21   JsonMapper.deserialize
test/model_test.dart 12:26                        main.<fn>

I think, the JsonMapper need to check the jsonMap is null before instantiating object.(json_mapper.dart 445)

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.

RFC 6901 JSON pointer support for mapping names/paths

The NASA Images and Videos public API returns model with a complex structure:

{
  "href":"https://images-assets.nasa.gov/image/PIA22161/collection.json",
  "data":[
    {
      "date_created":"2018-01-09T00:00:00Z",
      "media_type":"image",
      "center":"JPL",
      "nasa_id":"PIA22161",
      "title":"Investigating Mars: Candor Chasma",
      "secondary_creator":"NASA/JPL-Caltech/ASU",
      "description":"This image shows part of western Candor and the erosion of a large mesa. Layered materials are visible throughout the image. The dark material with the linear appearance in the middle of the image are sand dunes. Sand dunes are created by wind action. At the present time, wind is the active process shaping the surface.  Candor Chasma is one of the largest canyons that make up Valles Marineris. It is approximately 810 km long (503 miles) and has is divided into two regions - eastern and western Candor. Candor is located south of Ophir Chasma and north of Melas Chasma. The border with Melas Chasma contains many large landslide deposits. The floor of Candor Chasma includes a variety of landforms, including layered deposits, dunes, landslide deposits and steep sided cliffs and mesas. Many forms of erosion have shaped Chandor Chasma. There is evidence of wind and water erosion, as well as significant gravity driven mass wasting (landslides).  The Odyssey spacecraft has spent over 15 years in orbit around Mars, circling the planet more than 69000 times. It holds the record for longest working spacecraft at Mars. THEMIS, the IR/VIS camera system, has collected data for the entire mission and provides images covering all seasons and lighting conditions. Over the years many features of interest have received repeated imaging, building up a suite of images covering the entire feature. From the deepest chasma to the tallest volcano, individual dunes inside craters and dune fields that encircle the north pole, channels carved by water and lava, and a variety of other feature, THEMIS has imaged them all. For the next several months the image of the day will focus on the Tharsis volcanoes, the various chasmata of Valles Marineris, and the major dunes fields. We hope you enjoy these images!  Orbit Number: 6245 Latitude: -5.77639 Longitude: 284.339 Instrument: VIS Captured: 2003-05-12 14:49  https://photojournal.jpl.nasa.gov/catalog/PIA22161",
      "keywords":[
        "2001 Mars Odyssey",
        "Mars"
      ],
      "description_508":"This image captured by NASA's 2001 Mars Odyssey spacecraft shows part of western Candor and the erosion of a large mesa. Layered materials are visible throughout the image."
    }
  ],
  "links":[
    {
      "rel":"preview",
      "href":"https://images-assets.nasa.gov/image/PIA22161/PIA22161~thumb.jpg",
      "render":"image"
    }
  ]
}

It's really great this library supports nested objects like @JsonProperty(name: 'baz/items') but does it support List as well?

In my use case, I'd like to do something like: @JsonProperty(name: 'data/0/title').

Can not serialize Map<String, T>

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

Does this work for "private" fields?

Hello Alexander,

at first, I am so grateful, that someone developed a JSON mapper, without this awful boilerplate code. Those mixins and abstract just for serialization is a step back and by far no innovation. So thank you!

So to my problem:
I have a lot of classes like the one below. The problem is, those are my business objects, so I need a serializer, who can also serialize private fields. As I looked in the source code of dart-json-mapper this feature seems not to exist right now. Of course, I only want to use the serializer in the same library. Otherwise, we wouldn't have access to them.

Would be great to have this, because this is a dealbreaker for me, and should be easy to set up. At least as an option.

class User extends Model implements IImmutableUser {
  String _email;

  String get email => _email; // Here happens, more business stuff in general.
  set email(String email) => _email = email; // Here happens, more business stuff in general.
}

Hopefully we find a solution on this :).

Thanks,

Niklas

Wrong targeted data when parsing

Hi,
I have a special case:

@jsonSerializable
class A {
  @JsonProperty(name: "content")
  B content;
}

@jsonSerializable
class B {
  @JsonProperty(name: "content")
  List<A> content;
}

When I parse a json like:

{
      "content": {
           "content": []
      }
}

I expect to have A as a root object. But there is an error that it use B's content as A's content.
After debugging, I've realized that here is the problem:

dynamic getPropertyValue(String name) {
    dynamic result;
    _isPathExists(_getPath(name), (m, k) {
      result = (m is Map && m.containsKey(k)) ? m[k] : m;
    });
    return result;
  }

To fix it, should it only be:

result = m;

? (Because m[k] is B's content).

Thank you

Too many positional arguments on reflectable-2.2.1

Hi,

I got this error message that seems related to a dependency :

[SEVERE] Failed to snapshot build script .dart_tool/build/entrypoint/build.dart.
This is likely caused by a misconfigured builder definition.
[SEVERE] ../../flutter/.pub-cache/hosted/pub.dartlang.org/reflectable-2.2.1/lib/src/builder_implementation.dart:5207:46: Error: Too many positional arguments: 0 allowed, but 1 found.Try removing the extra positional arguments.  InterfaceType get type => InterfaceTypeImpl(this);                                             ^../../flutter/.pub-cache/hosted/pub.dartlang.org/analyzer-0.39.4/lib/src/dart/element/type.dart:705:3: Context: Found this candidate, but the arguments don't match.  InterfaceTypeImpl({  ^^^^^^^^^^^^^^^^^../../flutter/.pub-cache/hosted/pub.dartlang.org/reflectable-2.2.1/lib/src/builder_implementation.dart:5214:24: Error: Too many positional arguments: 0 allowed, but 1 found.Try removing the extra positional arguments.      InterfaceTypeImpl(this);                       ^../../flutter/.pub-cache/hosted/pub.dartlang.org/analyzer-0.39.4/lib/src/dart/element/type.dart:705:3: Context: Found this candidate, but the arguments don't match.  
InterfaceTypeImpl({  ^^^^^^^^^^^^^^^^^
pub finished with exit code 78

Overiding the dependency :
dependency_overrides:
reflectable: 2.2.0

[INFO] Running build completed, took 7.7s

Any idea why ?

Thanks

Serializing null DateTime

Hi, the serialization of a null DateTime is wrong. It is serialized as { "data": "null" } (as String) instead of { "data": null }.

Below is a test case:

@jsonSerializable
class Model {
    DateTime data;
}


void main() {
  initializeReflectable();
  var a = Model();
  var b = JsonMapper.toJson(a);
  print(b);
  var c = JsonMapper.fromJson<Model>(b);
}

The last line, correctly, throws the error:

FormatException (FormatException: Invalid date format null)

Deserializing with base type as parameter using @Json(typeNameProperty: ' ') throws exception

This currently throws the following exception:

It seems your class 'User' has not been annotated with @jsonSerializable

But we are never serializing or deserializing the User class itself, only the UserImpl class. The UserImpl class picks up the type from the JSON. So there would be no need to annotate the user class, when Serializer can detect the concret type in the JSON?

  test('User can be serialized and deserialized', () {
    User user = UserBuilder().build();
    var map = JsonMapper.toMap(user);
    var newUser = JsonMapper.fromMap<User>(map);
  });
abstract class User extends DataModel {
  final Email email;

  User copyWith({
    String id,
    Email email,
  });

  factory User(String id) =  UserImpl.newUser;
}

@Json(typeNameProperty: 'type')
@jsonSerializable
class UserImpl extends DataModel implements User {
  final Email email;

  UserImpl(
      {String id,
      this.email})
      : super(id: id);

  factory UserImpl.newUser(
      String id) {
    return UserImpl(
      id: id,
    );
  }

  @override
  User copyWith(
      {Email email,
      String id}) {
    return UserImpl(
        id: id ?? this.id,
        email: email ?? this.email);
  }
}

downGrade intl PKG to ver : 16.0.0

Because dart_json_mapper >=1.3.3 depends on intl ^0.16.1 and every version of flutter_localizations from sdk depends on intl 0.16.0, dart_json_mapper >=1.3.3 is incompatible with flutter_localizations from sdk.

because flutter_localization depend on intl : 16.0.0 , so I can't localize my app.
because I want to use

JsonMapper().useAdapter(
    JsonMapperAdapter(
      valueDecorators: {
        typeOf<List<ContactModel>>(): (value) => value.cast<ContactModel>()
      },
    ),
  );

also because of #36

Error while using this with Flutter Firestore

I'm using this library to deserialize Firestore documents, and I'm seeing the following error whenever I try to call JsonMapper.fromMap<T>(data);

E/flutter (23945): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type '_ImmutableMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>'
E/flutter (23945): null
E/flutter (23945): [ERROR:flutter/shell/common/shell.cc(199)] Dart Error: Unhandled exception:
E/flutter (23945): type '_ImmutableMap<dynamic, dynamic>' is not a subtype of type 'Map<String, String>'
E/flutter (23945): #0      _rootHandleUncaughtError.<anonymous closure> (dart:async/zone.dart:1114:29)
E/flutter (23945): #1      _microtaskLoop (dart:async/schedule_microtask.dart:43:21)
E/flutter (23945): #2      _startMicrotaskLoop (dart:async/schedule_microtask.dart:52:5)

I was seeing a similar issue with json_serializable, but I was able to fix it with an option they provided in my build.yaml: https://pub.dev/documentation/json_annotation/latest/json_annotation/JsonSerializable/anyMap.html

Add ignore 'implementation_imports'

In the generated main.reflectable.dart, I presume the following can be added to reduce the numbers of problems :
// ignore_for_file: implementation_imports

Regards

Hot to avoid CircularReferenceError

I need to serialize the same object multiple times into the same object. It is a stamp, which is copied to both a start field and an edit field. However I get a CircularReferenceError

Can`t use JsonMapper in production build for web platform

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

Serialize and Deserialize from and to Map<String, dynamic>

Hey @k-paxian,

as I started developing my base class repository for Firestore, another issue came up. The type of document data in the Firestore for Flutter package is always of type Map<String, dynamic>. Because Firestore is the go-to for Flutter and Angular developer, additional serialization and deserialization methods who can handle a Map<String, dynamic> type, would be a huge benefit. I would suggest the following signature:

static Map<String, dynamic> JsonMapper.serializeToMap(Object model);
static T JsonMapper.deserializeFromMap<T>(Map<String, dynamic> map);

Here is a simplified example of how I would use this in my repository:

class Repository<TModel extends DataModel> {
  CollectionReference collectionReference;

  Repository(this.collectionReference);

  Future update(TModel model) async {
    Map<String, dynamic> map = JsonMapper.serializeToMap(model);
    await collectionReference.document(model.id).updateData(map);
  }

  Future<TModel> get(String id) async {
    var snapshot = await collectionReference.document(id).get();
    return JsonMapper.deserializeFromMap<TModel>(snapshot.data)..id = id;
  }
}

What do you think about this?

Niklas

Collect Unmapped Properties

Support alternative for @JsonAnySetter from Java jackson

{
    "firstName": "",
    "lastName": "",
    "extraProp": 1,
    "extraProp2": 2
}

=>

@jsonSerializable
class Object {
   String firstName;
   String lastName;

   @jsonAnySetter
    unmappedProperty(String name, dynamic value) {
       // collect into internal map
    }
}

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.

How to handle multiple models in difference files?

for example, there are two models in two files
modelA.dart

@jsonSerializable 
class A {
    int id;
    String name;
}
//It's seem must set this method in model file, right? if I not set it ,it can't generate the code :(
main() {
  initializeReflectable(); 
}

modelB.dart

@jsonSerializable 
class B {
    int id;
    String name;
}
//It's seem must set this method in model file, right?
main() {
  initializeReflectable(); 
}

and the program main file main.dart

void main() {
    //how can I init the json model here?
}

so, how can I reference these files and call the initializeReflectable();

thanks!

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?

Featurerequest: Typenamehandling

Sorry for coming up here again :). But...

I am also in a situation where I need to handle something like this:

@jsonSerializable
class Business {
  String name;
}

@jsonSerializable
class Hotel extends Business {
  int stars;
  Hotel(this.stars);
}

@jsonSerializable
class Startup extends Business {
  int userCount;

  Startup(this.userCount);
}

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

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

main() {
  var jack = Stakeholder("Jack", <Business>[Startup(10), Hotel(4)]);
  var json = JsonMapper.serialize(jack);
  var jack2 = JsonMapper.deserialize<Stakeholder>(json);
  jack2.businesses[0] is Startup; //Should be true
  jack2.businesses[1] is Hotel; //Should be true
}

The serializer should be able to, get the current type, save it in a JSON property, for example, "$type". And can create those types based on the "$type" property.

Newtonsoft.NET is handling this very pretty: https://www.newtonsoft.com/json/help/html/SerializeTypeNameHandling.htm

Because you are using a kind of "reflection", and not this complicated code generation JSON stuff, this should be possible right?

If you need any help, I am willing to do, but I've never contributed to an open source project, so you are dealing with a noob :).

Thanks and a happy day,

Niklas

Provide default values?

Is there a way to provide default values? I'm migrating from json_serializable, and I was doing things like:

  @JsonKey(defaultValue: []) final List<Person> guardians;
  @JsonKey(defaultValue: {}) final Map<String, String> addresses;
  @JsonKey(defaultValue: false) final bool isRead;
  @JsonKey(defaultValue: false) final bool isArchived;

I'm not really seeing an equivalent way to do that with this library.

How to register List of List

Hello,

Thank you again for the great package.

I have a list of list coming from API as in the following picture.

image

I couldn't register the type. I get the following error.

E/flutter (30766): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<dynamic>'
E/flutter (30766): #0      JsonMapper.deserializeObject (package:dart_json_mapper/src/mapper.dart:475:21)
E/flutter (30766): #1      JsonMapper.deserializeObject.<anonymous closure> (package:dart_json_mapper/src/mapper.dart:478:26)
E/flutter (30766): #2      MappedListIterable.elementAt (dart:_internal/iterable.dart:417:29)

How can I register a list of lists of objects?

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

Parameter enumValues is probably ignored

Given this dummy code fragment:

import 'package:dart_json_mapper/json_mapper.dart';

import 'main.reflectable.dart';

enum OperationType { insert, update, remove }

const operationTypeValues = ["__insert", "__update", "__remove"];

@jsonSerializable
class XYZ {
  @JsonProperty(enumValues: operationTypeValues)
  OperationType operation = OperationType.insert;
}


void main() {
  initializeReflectable();


  var xyz = XYZ()..operation = OperationType.update;
  print(JsonMapper.serialize(xyz));

}

it will print

{
"operation": "OperationType.update"
}

So what is wrong? Why is actually enumValues needed?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.