Skip to content

Commit

Permalink
add null-safety close #4
Browse files Browse the repository at this point in the history
  • Loading branch information
robert-virkus committed Mar 3, 2021
1 parent a50a264 commit 50cf9f7
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 126 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
@@ -1,3 +1,7 @@
## 1.3.0

- Add null-safety.

## 1.2.0

- Add option to directly serialize/deserialize list values with `Serializer.serializeList(...)` and `Serializer.deserializeList(...)`.
Expand Down
8 changes: 6 additions & 2 deletions README.md
Expand Up @@ -7,7 +7,7 @@ Add this dependency your pubspec.yaml file:

```
dependencies:
enough_serialization: ^1.1.0
enough_serialization: ^1.3.0
```
The latest version or `enough_serialization` is [![enough_serialization version](https://img.shields.io/pub/v/enough_serialization.svg)](https://pub.dartlang.org/packages/enough_serialization).

Expand All @@ -19,7 +19,7 @@ You can choose between two serialization modes:
1. Extend `SerializableObject` or implement `Serializable` for full control and complex cases. You have to store your field values in a dynamic map, however.
2. Implement `OnDemandSerializable` to only write and read your field values to/from a dynamic map when needed. This comes with limitations when having deep nested structures - in that case any serialization names of complex fields must be unique.

### Serilization with `Serializable` and `SerializableObject`
### Serialization with `Serializable` and `SerializableObject`

The easiest way is
* extend `SerializableObject`
Expand Down Expand Up @@ -398,6 +398,10 @@ Please file feature requests and bugs at the [issue tracker][tracker].

[tracker]: https://github.com/Enough-Software/enough_serialization/issues

## Null-Safety

enough_convert is null-safe from v1.3.0 onward.

## License

Licensed under the commercial friendly [MIT License](LICENSE).
84 changes: 42 additions & 42 deletions example/enough_serialization_example.dart
Expand Up @@ -13,14 +13,14 @@ void main() {
}

class SimpleArticle extends SerializableObject {
String get name => attributes['name'];
set name(String value) => attributes['name'] = value;
String? get name => attributes['name'];
set name(String? value) => attributes['name'] = value;

int get price => attributes['price'];
set price(int value) => attributes['price'] = value;
int? get price => attributes['price'];
set price(int? value) => attributes['price'] = value;

double get popularity => attributes['popularity'];
set popularity(double value) => attributes['popularity'] = value;
double? get popularity => attributes['popularity'];
set popularity(double? value) => attributes['popularity'] = value;
}

void simpleExample() {
Expand Down Expand Up @@ -50,11 +50,11 @@ class ArticleWithEnum extends SerializableObject {
value is ArticleArea ? value.index : ArticleArea.values[value];
}

ArticleArea get area => attributes['area'];
set area(ArticleArea value) => attributes['area'] = value;
ArticleArea? get area => attributes['area'];
set area(ArticleArea? value) => attributes['area'] = value;

String get name => attributes['name'];
set name(String value) => attributes['name'] = value;
String? get name => attributes['name'];
set name(String? value) => attributes['name'] = value;
}

void enumExample() {
Expand All @@ -79,23 +79,23 @@ class Article extends SerializableObject {
value is ArticleArea ? value.index : ArticleArea.values[value];
}

ArticleArea get area => attributes['area'];
set area(ArticleArea value) => attributes['area'] = value;
ArticleArea? get area => attributes['area'];
set area(ArticleArea? value) => attributes['area'] = value;

String get name => attributes['name'];
set name(String value) => attributes['name'] = value;
String? get name => attributes['name'];
set name(String? value) => attributes['name'] = value;

int get price => attributes['price'];
set price(int value) => attributes['price'] = value;
int? get price => attributes['price'];
set price(int? value) => attributes['price'] = value;
}

class ElectronicsArticle extends Article {
ElectronicsArticle() {
area = ArticleArea.electronics;
}

String get recommendation => attributes['recommendation'];
set recommendation(String value) => attributes['recommendation'] = value;
String? get recommendation => attributes['recommendation'];
set recommendation(String? value) => attributes['recommendation'] = value;
}

class MusicArticle extends Article {
Expand All @@ -104,18 +104,18 @@ class MusicArticle extends Article {
objectCreators['band'] = (map) => Band();
}

Band get band => attributes['band'];
set band(Band value) => attributes['band'] = value;
Band? get band => attributes['band'];
set band(Band? value) => attributes['band'] = value;
}

class Band extends SerializableObject {
String get name => attributes['name'];
set name(String value) => attributes['name'] = value;
String? get name => attributes['name'];
set name(String? value) => attributes['name'] = value;

int get year => attributes['year'];
set year(int value) => attributes['year'] = value;
int? get year => attributes['year'];
set year(int? value) => attributes['year'] = value;

Band({String name, int year}) {
Band({String? name, int? year}) {
this.name = name;
this.year = year;
}
Expand All @@ -126,7 +126,7 @@ class Order extends SerializableObject {
objectCreators['articles'] = (map) => <Article>[];
// the list articles contains different Articel instances depending on the specified area field:
objectCreators['articles.value'] = (map) {
final int areaIndex = map['area'];
final int areaIndex = map!['area'];
final area = ArticleArea.values[areaIndex];
switch (area) {
case ArticleArea.electronics:
Expand All @@ -138,8 +138,8 @@ class Order extends SerializableObject {
};
}

List<Article> get articles => attributes['articles'];
set articles(List<Article> value) => attributes['articles'] = value;
List<Article>? get articles => attributes['articles'];
set articles(List<Article>? value) => attributes['articles'] = value;
}

void complexExample() {
Expand Down Expand Up @@ -169,16 +169,16 @@ void complexExample() {
'{"area": 1, "name": "The white album", "price": 1899, "band": {"name": "Beatles", "year": 1962}}]}';
final deserializedOrder = Order();
serializer.deserialize(inputJson, deserializedOrder);
for (var i = 0; i < deserializedOrder.articles.length; i++) {
final article = deserializedOrder.articles[i];
for (var i = 0; i < deserializedOrder.articles!.length; i++) {
final article = deserializedOrder.articles![i];
print('$i: area: ${article.area}');
print('$i: name: ${article.name}');
print('$i: price: ${article.price}');
if (article is ElectronicsArticle) {
print('$i: recommendation: ${article.recommendation}');
} else if (article is MusicArticle) {
print('$i: band-name: ${article.band.name}');
print('$i: band-year: ${article.band.year}');
print('$i: band-name: ${article.band!.name}');
print('$i: band-year: ${article.band!.year}');
}
}
}
Expand All @@ -190,11 +190,11 @@ class MappedArticle extends SerializableObject {
(value) => value is int ? value.toString() : int.parse(value);
}

String get name => attributes['name'];
set name(String value) => attributes['name'] = value;
String? get name => attributes['name'];
set name(String? value) => attributes['name'] = value;

Map<int, String> get newsByYear => attributes['news-by-year'];
set newsByYear(Map<int, String> value) => attributes['news-by-year'] = value;
Map<int, String>? get newsByYear => attributes['news-by-year'];
set newsByYear(Map<int, String>? value) => attributes['news-by-year'] = value;
}

void mapExample() {
Expand All @@ -216,14 +216,14 @@ void mapExample() {
final deserializedArticle = MappedArticle();
serializer.deserialize(inputJson, deserializedArticle);
print('deserialized article: ${article.name}');
for (final key in article.newsByYear.keys) {
print('$key: ${article.newsByYear[key]}');
for (final key in article.newsByYear!.keys) {
print('$key: ${article.newsByYear![key]}');
}
}

class OnDemandArticle implements OnDemandSerializable {
String name;
Map<int, String> newsByYear;
String? name;
Map<int, String>? newsByYear;

String serialize() {
final serializer = Serializer();
Expand Down Expand Up @@ -283,7 +283,7 @@ void onDemandExample() {
final deserializedArticle = OnDemandArticle();
deserializedArticle.deserialize(inputJson);
print('deserialized article: ${article.name}');
for (final key in article.newsByYear.keys) {
print('$key: ${article.newsByYear[key]}');
for (final key in article.newsByYear!.keys) {
print('$key: ${article.newsByYear![key]}');
}
}
27 changes: 14 additions & 13 deletions lib/src/serializable.dart
Expand Up @@ -38,7 +38,7 @@ abstract class Serializable {
/// objectCreators['my-map.value'] = (map) => MySerializable();
///```
///
Map<String, dynamic Function(Map<String, dynamic>)> get objectCreators;
Map<String, dynamic Function(Map<String, dynamic>?)> get objectCreators;
}

/// Supports classes with normal fields.
Expand Down Expand Up @@ -84,7 +84,7 @@ abstract class OnDemandSerializable {
class SerializableObject implements Serializable {
final Map<String, dynamic> _attributes = {};
final Map<String, dynamic Function(dynamic)> _transformers = {};
final Map<String, dynamic Function(Map<String, dynamic>)> _objectCreators =
final Map<String, dynamic Function(Map<String, dynamic>?)> _objectCreators =
{};
@override
Map<String, dynamic> get attributes => _attributes;
Expand All @@ -93,7 +93,7 @@ class SerializableObject implements Serializable {
Map<String, dynamic Function(dynamic)> get transformers => _transformers;

@override
Map<String, dynamic Function(Map<String, dynamic>)> get objectCreators =>
Map<String, dynamic Function(Map<String, dynamic>?)> get objectCreators =>
_objectCreators;
}

Expand All @@ -117,7 +117,7 @@ class Serializer {

/// Serializes an OnDemandSerializable object.
String serializeOnDemand(OnDemandSerializable onDemandSerializable,
{Map<String, dynamic Function(dynamic)> transformers}) {
{Map<String, dynamic Function(dynamic)>? transformers}) {
if (onDemandSerializable is Serializable) {
final serializable = onDemandSerializable as Serializable;
onDemandSerializable.write(serializable.attributes);
Expand Down Expand Up @@ -154,8 +154,8 @@ class Serializer {
/// Specify [transformers] to transform values such as enums or non-String Map keys.
/// Specify [objectCreators] when you have Lists, Map or nested objects.
void deserializeOnDemand(String jsonText, OnDemandSerializable target,
{Map<String, dynamic Function(dynamic)> transformers,
Map<String, dynamic Function(Map<String, dynamic>)> objectCreators}) {
{Map<String, dynamic Function(dynamic)>? transformers,
Map<String, dynamic Function(Map<String, dynamic>?)>? objectCreators}) {
if (target is Serializable) {
final serializable = target as Serializable;
deserialize(jsonText, serializable);
Expand All @@ -172,7 +172,7 @@ class Serializer {
target.read(genericSerializable.attributes);
}

void _serializeAttributes(Serializable parent,
void _serializeAttributes(Serializable? parent,
final Map<String, dynamic> attributes, final StringBuffer buffer) {
buffer.write('{');
var writeSeparator = false;
Expand All @@ -188,7 +188,7 @@ class Serializer {
buffer.write('}');
}

void _serializeValue(Serializable parent, final String key,
void _serializeValue(Serializable? parent, final String? key,
final dynamic value, final StringBuffer buffer) {
if (value == null) {
buffer.write('null');
Expand All @@ -215,7 +215,7 @@ class Serializer {
} else if (value is Map<String, dynamic>) {
_serializeAttributes(parent, value, buffer);
} else if (value is Map) {
final keyTransformer = parent.transformers['$key.key'];
final keyTransformer = parent!.transformers['$key.key'];
if (keyTransformer == null) {
throw StateError(
'Invalid map with non-String keys encountered, unable to serialize: "$key": $value. Define a corresponding transformer "$key.key" to transform the keys of this map to String.');
Expand All @@ -229,12 +229,12 @@ class Serializer {
_serializeAttributes(value, value.attributes, buffer);
} else if (value is OnDemandSerializable) {
final genericSerializable = SerializableObject();
genericSerializable.transformers.addAll(parent.transformers);
genericSerializable.transformers.addAll(parent!.transformers);
value.write(genericSerializable.attributes);
_serializeAttributes(
genericSerializable, genericSerializable.attributes, buffer);
} else {
final transform = parent.transformers[key];
final transform = parent!.transformers[key!];
if (transform == null) {
throw StateError(
'Invalid value encountered, unable to serialize: "$key": $value. Define a corresponding transformer.');
Expand All @@ -246,7 +246,7 @@ class Serializer {

void _deserializeAttributes(
final Map<String, dynamic> json, final Serializable object,
[String parentKey]) {
[String? parentKey]) {
for (final key in json.keys) {
final value = json[key];
object.attributes[key] = _deserializeValue(object, key, value);
Expand Down Expand Up @@ -279,7 +279,8 @@ class Serializer {
return listValue;
} else if (value is Map<String, dynamic>) {
// this is a nested object or a nested map
final function = parent == null ? null : parent.objectCreators[key];
final dynamic Function(Map<String, dynamic>)? function =
parent == null ? null : parent.objectCreators[key];
if (function == null) {
throw StateError(
'Unknown map or serializable for object "$key": please define a corresponding objectCreator.');
Expand Down
8 changes: 4 additions & 4 deletions pubspec.yaml
@@ -1,14 +1,14 @@
name: enough_serialization
description: Runtime solution for serializing to and deserializing from JSON. enough_serialization also supports non-generic lists, enums and complex nested objects.
version: 1.2.0
version: 1.3.0
homepage: https://www.github.com/enough-software/enough_serialization

environment:
sdk: '>=2.9.3 <3.0.0'
sdk: '>=2.12.0 <3.0.0'

#dependencies:
# path: ^1.7.0

dev_dependencies:
pedantic: ^1.9.0
test: ^1.14.4
pedantic: ^1.11.0
test: ^1.16.5

0 comments on commit 50cf9f7

Please sign in to comment.