Build JSON serializable classes without code gen or mirrors

This library provides a way to create modeles easily usable from code that can be serialized into/from JSON.

Instead of directly using variables of the corresponding types we use 'fields'. A 'field' has a type.

Models

Every class that must be json serializable from this library is a subclass of Model. A model can

  • Be serialized to JSON
  • Be instanciated from parsed JSON
  • Have fields with a type of Model
  • Be able to correctly create it sub model fields from JSON

If you have a Model that has some field with type of other model, this will work fine. Instanciating the parent from json will also instanciate the child correctly.

Example

class Person extends ModelBuilder {
  Person(super.json);

  // IMPORTANT: you must put all your field here to ensure that it will work
  @override
  Iterable<Field> get fields => [age, name, address, properties, numbersArray, otherHomes];

  StringField get name => stringField('name');
  IntField get age => intField('age');

  ModelField<Address> get address => modelField('address'); // It will parse json to instance of Address 

  ModelField<ModelMap> get properties => mapField('props'); // Equivalent to: Map<String, dynamic>

  ModelField<ModelList> get numbersArray => listField('numbers_array', type: int); //Equivalent to List<int>

  ModelField<ModelMap> get otherHomes => mapField('homes', type: Address); // Equivalent to: Map<String, dynamic>
}

class Address extends ModelBuilder {
  Address(super.json);

  @override
  Iterable<Field> get fields => [street, number, city, state, country];

  StringField get street => stringField('street');
  IntField get number => intField('number');
  StringField get city => stringField('city');
  StringField get state => stringField('state');
  StringField get country => stringField('country');
}

void main() {
  // IMPORTANT: you must register Model types
  Model.register('person', (json) => Person(json));
  Model.register('address', (json) => Address(json));

  // Can fill model from JSON
  final me = Person({
    'name': 'Tomás',
    'age': 20,
    'address': {
      'street': 'Some Street',
      'city': 'Some City',
      'country': 'Dream Land',
    }
  });

  // Can know if field is filled or not
  if(!me.numbersArray.isLoaded) {
      // Set property one by one
      me.numbersArray.set([1, 2, 3, 4, '5', 'invalid, this will ve ignored' ]);
  }

  me.age.set(21);

  // Print single property
  print('Name: ${me.name.value}');

  // Convert to json
  print(me.toJson());

  // If using a 'ModelField<SomeType>', use current instead of value
  // It will have the corresponding type 
  // The value readed here was created from json as 'Address'
  Address address = me.address.current;

}

List

To create a field with type list use

// List of int (cannot have any value type other than int)
ListField get numbersArray => listField('numbers_array', type: int); 

// Can have any value type
// ListField is equivalent to ModelField<ModelList>
ModelField<ModelList> get things => listField('things_array'); 

// Can have a model as type
ListField get homes => listField('homes', type: Address); 


// Read value
int index = 3;
someone.homes.current?[index]?.value
someone.homes.current?.get(index)?.value

// Weite value
someone.homes.current?.add({ ... })
someone.homes.current?.add(Address({ ... }))
someone.homes.current?[1] = Address({ ... })
someone.homes.current?[someone.homes.current.length] = { ... } // It will be converted from json to Address
someone.homes.current?.set(2, Address({ ... }))
someone.homes.current?.set(2, { ... })

Map

To create a field with type mape use

// List of int (cannot have any value type other than int)
MapField get numbersArray => mapField('amounts', type: int); 

// Can have any value type
MapField get things => mapField('things'); 

// Can have a model as type
// MapField is equivalent to ModelField<ModelMap>
ModelField<ModelMap> get homes => mapField('homes', type: Address); 


// Read value
someone.homes.current?['some_home_id']?.value
someone.homes.current?.get('some_home_id')?.value

// Weite value
someone.homes.current?['some_home_id'] = Address({ ... })
someone.homes.current?['some_home_id'] = { ... } // It will be converted from json to Address
someone.homes.current?.set('some_home_id', Address({ ... }))
someone.homes.current?.set('some_home_id', { ... })

All types

class ModelUsingAllPosibleTypes extends ModelBuilder {
  ModelUsingAllPosibleTypes(super.json);

  @override
  Iterable<Field> get fields => [date, integer, number, name, correct, arrayOfAny, numbers, friends, properties, scores, playersByName];

  DateTimeField get date => dateTimeField('date');

  IntField get integer => intField('integer');

  DoubleField get number => doubleField('number');

  StringField get name => stringField('name');

  BoolField get correct => boolField('is_correct');

  ListField get arrayOfAny => listField('things');

  ListField get numbers => listField('numbers', type: double);

  ListField get friends => listField('friends', type: Human);

  MapField get properties => mapField('properties');

  MapField get scores => mapField('scores', type: int);

  MapField get playersByName => mapField('players', type: Human);
}