DSON is a dart library which converts Dart Objects into their JSON representation.
This library was initially a fork from Dartson. Now it contains some differences:
-
Dartson uses custom transformers to convert objects to JSON. This produce faster and smaller code after dart2Js. Instead DSON uses [serializable]() and [built_mirrors]() libraries. This should produce code as fast and small as Dartson transformer.
-
DSON has the ability to serialize cyclical objects by mean of
expand
parameter, which allows users to specify how deep in the object graph they want to serialize. -
DSON has the ability to exclude attributes for serialziation in two ways.
-
Using
@ignore
over every attribute. This make excluding attributes too global and hardcoded, so users can only specify one exclusion schema. -
Using
exclude
map as parameter fortoJson
method. This is more flexible, since it allows to have many exclusion schemas for serialization.
-
-
DSON uses the annotation
@serializable
instead@entity
which is used by Dartson.
1- Create a new dart project.
2- Add dependencies to pubspec.yaml
...
dependencies:
#...
dson: any # replace for latest version
#...
dev_dependencies:
#...
build_runner: any
build_web_compilers: any
#...
3- Create/edit bin/main.dart
or web/main.dart
and add the code shown in any of the samples below.
4- Run either dart run build_runner build
, or dart run build_runner watch
, or dart run build_runner serve
in the console
To convert objects to JSON strings you only need to use the toJson
function, annotate the object with @serializable
and pass the object
to the toJson
function as parameter:
link:example/bin/object_to_json.dart[role=include]
To convert objects to Maps you only need to use the toMap
function, annotate the object with @serializable
and pass the object
to toMap
function as parameter:
link:example/bin/object_to_map.dart[role=include]
To serialize objects that contains Cyclical References it would be needed to use the annotation @cyclical
. If this annotation is present and the expand
variable is not set then the non-primitive objects are not going to be parsed and only the id (or hashmap if the object does not contains id) is going to be present. Let’s see next example:
link:example/bin/serialize_cyclical.dart[role=include]
as you can see employee has an address, and the address has an owner of type Employee. If the property id
is not present in the object then it is going to take the hashcode
value from the object as reference. And finally, the expand
parameter passed to serialize function tells serializer how deep you want to go throw the reference. This help us not only to avoid cyclical reference, but to determine what referenced objects should be serialized.
The same applies for lists:
link:example/bin/serialize_cyclical_list.dart[role=include]
Without the annotation @cyclical
the program is going to throw a stack overflow error caused by the serializing of cyclical objects.
To exclude parameter from being serialized we have two options the first option is using @ignore
over the attribute to ignore. However this approach is too global. What I want to say with this is that the attribute is going to be ignored always.
Another way to exclude attributes is adding the parameter exclude
to serialize
function. In this way we only exclude those attributes during that serialization.
link:example/bin/exclude_attributes.dart[role=include]
To convert JSON strings to objects you only need to use the fromJson
and fromJsonList
functions and pass the json
string to deserialize and the Type
of the object as parameters:
link:example/bin/json_to_object.dart[role=include]
To extends objects that are going to be serializable you will need to add the comment:
// ignore: mixin_inherits_from_not_object
This is to advice the analyzer to ignore the error caused by inheriting from an object that is not a mixin. For example:
link:example/bin/extend_serializables.dart[role=include]
To make an immutable class to be able to serialize/deserialize you only need to declare it with a constructor which only contains final parameters. For example:
link:example/bin/immutable_objects.dart[role=include]
Be sure the names of the fields and constructor parameters match. If they do not match, then the deserialized object will contain attributes with null value
Serializing generic objects is pretty simple, you only need to call the toJson
function as fallow:
link:example/bin/generics.dart[role=include]
Deserialization however is more complicated. You need to specify a list of factories and types starting with the top class. In the same list of factory you will also need to specify a map of factories for each generic attribute, for example:
link:example/bin/generics.dart[role=include]
the full code of the example should look as fallow:
link:example/bin/generics.dart[role=include]
At this point you could be thinking how the held you know what should it be te correct factory list to convert from json. And maybe why we didn’t use something simpler like just passing the type like next:
Page<Person> page2 = fromJson(jsonStr, Page<Person>);
Sadly, it is not possible in dart to pass generic types as parameters, but in previous versions of the library this was possible just passing an array of types as fallow:
Page<Person> page2 = fromJson(jsonStr, [Page, Person]);
As you can see you only needed to convert <
and >
into brackets [
and ]
respectively and also add two more brackets at the start and end of the type.
However, that was only possible in previous versions of Dart SDK. The latest version does not allow to set values of dynamic types into the attributes of classes. So that, we need to specify the conversion process using factory functions and types as fallow:
-
First take the generic
Page<Person>
that will be converted from json. -
Replace the
<
and>
by brackets, so it should look as followsPage[Person]
. -
Add brackets at the start and end:
[Page[Person]]
-
Add commas before every internal start bracket:
[Page, [Person]]
-
Since
Person
is not a generic we remove the enclosing brackets:[Page, Person]
-
Replace
Page
by a factory function like next:
[() => Page<Person>(), Person]
-
If the generic would be a
List
orMap
the previous would be enough. However, the generic isPage
which means we need to know which is the attribute that handle the generic type, in this case isitems
. Knowing that we replacePerson
by a map with the names of the attributes as keys and a factory of the types as values. As a result we will have something like next:
{'items': List<Person>}
-
Remember that passing generic types as parameter is disallowed, so we need to convert it to an array of factories and types. Hence, you should have something like next:
{'items': [List, [Person]]}
-
Remember that we need to remove the brackets of non-generics as fallow:
{'items': [List, Person]}
-
here you can also notice that
List
is a generic, so it should be converted into factory function:
() => List<Person>.empty(growable: true)
-
then we replace this factory in the
items
value of the previous created map:
{'items': [() => List<Person>.empty(growable: true), Person]
-
And finally we replace this map in the previous list as fallow:
[() => Page<Person>(), {'items': [() => List<Person>.empty(growable: true), Person]}]
Extended Generics can also be handled by this library for example you can use next code:
link:example/bin/extend_generics.dart[role=include]
Please don’t edit README.md
, instead edit _README.adoc
then run next command:
asciidoctor -b docbook _README.adoc && pandoc -f docbook -t gfm _README.xml -o README.md
If you find any problem please create an issue
You can also help to maintain this project by creating a new pull request