Last updated:
0 purchases
entity serializer
Entity serializer #
Another package which make process of building data classes and serializers, a little less
frustrating. Unless you don't have very specific needs you probably want to stick with
freezed or json_serializable
another great tool which inspired me is dtogen check it out
before trying this one.
What problem this package is solving? #
Mostly my frustration :) I run into problem where I got several data classes and then I needed to
create a lot of DTO classes. Several for server-client JSON communication and another DTO to have
MongoDB storage. So when I got for example Foo data class I was forced to create FooJson and
FooDB plus make some magic to serialize in different way for Json and DB.
I fast got to moment when I need to create a lot of boilerplate code to keep all together and in the
end my frustration won. This package solves my problem, hope it will be useful to somebody else.
How it works? #
All data classes (POJO style) should be described in simple xml file. In mentioned file is also
definition for one or multiple serializers which can operate on data classes. Then you run builder
or use cli tool to generate .dart files which can be included in your project. From this moment
you can perform serialization / deserialization of data classes using dedicated serializers. So this
allows you to have for example Foo class and simply serialize it to Map<String, dynamic> for
Json and for DB, where in both cases some rules of serialization can be different. There are no
intermediate DTO classes used. Just extensions for List<dynamic> and Map<String, dynamic> plus
extensions for data classes.
Limitations #
At current moment (and probably future) there is no support to embedded collections. So it's not
possible to have List of Maps, nor Maps of Maps, etc. If you need such thing please wrap map in some
extra custom type.
There are probably a lot of other limitations which I'm not aware yet but... This package will be
developed/extended until all my needs are satisfied, but for sure it will not be constant developed
in the way freezed is. Feel free to make feature request or deliver Pull-Requests if you added
something which was useful for you.
CLI version #
Please note existence of cli_main.dart file. It allows you to compile and have this tool as a
stand alone application, not bound to build_runner. All needed information how to use it is
displayed when you call it with -h switch. For most situations you will probably call something
like:
cli_main -i data_model/list_test.xml -o sources/gen.dart
copied to clipboard
or
cli_main -s -i data_model/list_test.xml -o sources/
copied to clipboard
Xml description #
There are two approaches to describe model:
all in one file, use spec root node in xml
serializers and entities are scattered across several xml files.
Refer to next sections for more info.
Multiple XMLs (composition) #
If you want to split model into several xml files you need to act as follow:
Define master xml which will point into all components xml which should be included
Define xmls which will contain serializers definitions
Define xmls which will contain entities definitions.
Syntax of xmls for serializers and entities is described in further sections, and it's this same
as for single xml mode.
Multiple XMLs: master xml #
This file should contain root node named composition and then several include nodes. Here is
example:
<composition>
<include file="comp_serializer.xml"/>
<include file="comp_entities_1.xml"/>
<include file="comp_entities_2.xml"/>
</composition>
copied to clipboard
order of includes is not important, Their location is assumed to be in the same folder as master xml.
Multiple XMLs: serializer xml #
In this xml following rules must be meet:
Root node must be named serializers
Root node might contain only nodes serializer or serializerTemplate
Syntax of child nodes is described in other sections of this document.
Multiple XMLs: entities xml #
In this xml following rules must be meet:
Root node must be named entities
Root node might contain only nodes class, proxy or import
Syntax of child nodes is described in other sections of this document.
Single XML #
Here is brief description of xml format used to generate everything. Let's go with some examples
and descriptions:
<?xml version="1.0"?>
<spec>
<import package="package:any_package.dart"/>
<serializer name="Ser1"/>
<class name="Book">
<string name="name"/>
</class>
</spec>
copied to clipboard
This is the most simple xml which has any meaning. We are defining one serializer which will be
named Ser1 and one data class named Book. As expected Book class will have just one String
field named name. For class Book serialization extensions will be generated.
Node import #
If for any reason you need add extra imports to generated code import node comes in. There is
single mandatory attribute called package where you put what need to be imported. Each xml can
have multiple import nodes.
Node serializerTemplate #
This node act almost this same as node serializer, the main difference is that builder will not
generate any code for serializerTemplate. It is just a template to put common specializations
used across other serializers. Think about it as a parent class from which other serializers can
inherit. Each serializer can inherit specializations from serializerTemplate by placing it name
in inheritFrom attribute.
Node serializer #
Serializer node informs builder that some named serializer need to be generated for data classes.
At current serializer has following one attributes
name - mandatory, this name is used as prefix for code generation, and also can be put into
serializers attribute of xml node class.
inheritFrom - optional, default: empty. Coma separated names of serializerTemplate from which
specializations will be copied.
By default serializer can handle Dart types: int, double, String, bool and all entities
classes described in xml. Further specialization how to encode other classes can be done through
child nodes of serializer. See next paragraphs.
Serializer children: aliases
Each specialization per type can have aliases for fields. To add alias used for serialization /
deserialization of field just add attribute with name of field with '_' prefix, value is new name
to be used. Here is example:
<spec>
<serializer name="KeepDate" >
<keep type="DateTime"/>
<keep type="int" _justInt="i"/>
</serializer>
<class name="TestEntity" copyWith="false">
<DateTime name="date"/>
<DateTime name="noAlias"/>
<int name="justInt"/>
</class>
</spec>
copied to clipboard
as a result serializer KeepDate for type int will remap all fields named justInt into i.
Serializer children: keep
Serializers converts Dart object instance into Map<String, dynamic> using some rules. However
sometimes you want to keep given object instance without change. So you want to copy object into map
just as it is. For this purpose node keep exist, it contains only one attribute which is type.
If serializer find node with given type, it will just copy it. Example:
<serializer name="KeepDate" >
<keep type="DateTime"/>
</serializer>
copied to clipboard
Serializer children: specialization
For types which you want to be handled in a special way you should use node specialization. Here
if how it looks like:
<serializer name="EpocDate">
<specialization
type="DateTime"
outType="int"
serialization="dateTimeToEpoc"
deserialization="dateTimeFromEpoc"
import="package:entity_serializer/entity_serializer.dart"
/>
</serializer>
copied to clipboard
Here is description of attributes:
type - on which type this rule apply
outType - what is result of conversion done by function pointed by serialization attribute
serialization - name of function which should be used to convert type into outType
deserialization - name of function which should be used to convert outType into type
import - from what package/source file functions pointed by serialization and deserialization
can be imported, this is optional attribute
Node class #
Each data class which need to be generated need to be put into class node inside xml. Following
attributes are available for this node:
name - mandatory, tells what is name of new class, should be PascalCase typed.
copyWith - optional, default: true. Tells if new created class should have @CopyWith annotation
serializers - optional, default: null. List of all serializers which should be generated for this
class. If empty/null then all know serializers are used.
generateEntity - optional, default: true. Tells builder if Dart class should be generated. If false
then only serializers are generated. Class body need to be imported manually.
apiProxy - optional, default: empty. This is comma separated list of api proxies which should be
generated for this entity. See proxy node documentation for more info.
mixin - optional, default: empty. This is comma separated list of mixins which will be added class
definition.
To create fields for newly created class child-nodes described later can be added. Each child-node
support following attributes:
name - required, tells how field should be named, apply to Dart language naming restrictions
final - optional, default: true. If set make class field final.
comment - optional, default:null. If set comment per field can be included into code
example:
<?xml version="1.0"?>
<spec>
<class name="Book">
<string name="name"/>
<string name="author" final="false"/>
</class>
</spec>
copied to clipboard
will result in
@CopyWith()
class Book {
final String name;
String author;
MyType({
required this.name,
required this.author,
});
}
copied to clipboard
Additionally each node name can be prefixed with Optional word, this adds ? optional suffix for
generated field. Example
<?xml version="1.0"?>
<spec>
<class name="Book">
<string name="name"/>
<optionalString name="descr"/>
</class>
</spec>
copied to clipboard
will result in
@CopyWith()
class Book {
final String name;
final String? descr;
MyType({
required this.name,
this.descr,
});
}
copied to clipboard
class children: for plain Dart types
Following nodes are reserved for plain Dart types: int, double, bool, String
Here is example of usage in xml
<?xml version="1.0"?>
<spec>
<class name="Book">
<string name="str1"/>
<optionalString name="str2"/>
<int name="int1"/>
<optionalInt name="int2"/>
<double name="dbl1"/>
<optionalDouble name="dbl2"/>
<bool name="bool1"/>
<optionalBool name="bool2"/>
</class>
</spec>
copied to clipboard
class children: collection - list
For Dart List type specialized node is required, it has all properties described above plus
mandatory attribute innerType, refer to this example:
<class name="MyList">
<list name="integers" innerType="int"/>
<list name="customType" innerType="MyType"/>
<list name="dynamicType" innerType="dynamic"/>
<list name="expDynamicType" innerType="dynamic" expectOnly="MyType,MyOther"/>
</class>
copied to clipboard
It's worth noting that innerType can point to dynamic this will generate extra code to handle
all data types class from this xml. If you want to limit class detection in dynamic list then
attribute expectOnly comes to play. You can specify only those types you want to be handled.
class children: collection - map
For Dart Map type specialized node is required, it has all properties described above plus
two mandatory attributes keyType and valueType. Behaviour of valueType is this same as
innerType in List node. Here is simple example:
<class name="MyMap">
<map name="integers" keyType="String" valueType="int"/>
<map name="strings" keyType="String" valueType="String"/>
<map name="customType" keyType="String" valueType="MyType"/>
<map name="dynamicType" keyType="String" valueType="dynamic"/>
<map name="expDynamicType" keyType="String" valueType="dynamic" expectOnly="MyType,MyOther"/>
</class>
copied to clipboard
class children: field
Most universal child node of class node is field which allows you to put variable of any type
into class. It has two mandatory attributes name and type. And looks like:
<class name="Book" copyWith="false">
<field name="author" type="Author"/>
<field name="meta" type="Meta?"/>
<optionalField name="extra" type="String"/>
</class>
copied to clipboard
class children: any name
If you want to use your custom data class as a type for variable in other classes just put it name.
Please look into this small example:
<spec>
<class name="Book" copyWith="false">
<field name="author" type="Author"/>
<field name="meta" type="Meta"/>
<Meta name="anotherMeta"/>
<meta name="andAnotherMeta"/>
</class>
<class name="Author" copyWith="false">
<string name="name"/>
<string name="surname"/>
</class>
<class name="Meta" copyWith="false">
<String name="country"/>
</class>
</spec>
copied to clipboard
Node proxy #
When there is need to have methods required by other packages like for example retrofit which expect
to have json_serializable methods proxies enter the game. This feature allow generation of
methods expected by other packages. Currently there is support for following 3rd parties:
json_serializable
To enable api proxies add following xml node into your file.
<proxy name="myProxy" type="json_serializable" serializer="MyJson"/>
copied to clipboard
where:
name - mandatory, name of your proxy. This name need to be given in attribute apiProxy of node class
type - mandatory, predefined types which are supported by this package, listed above.
serializer - mandatory, name of serializer which should be wrapped in proxy generated methods.
After adding proxy node to xml, it is possible to add to any class node attribute apiProxy,
here is example:
<spec>
<serializer name="MyJson">
<specialization
type="DateTime"
serialization="dateTimeToIsoStr"
deserialization="dateTimeFromIsoStr"
import="package:entity_serializer/entity_serializer.dart"
/>
</serializer>
<proxy name="myProxy" type="json_serializable" serializer="MyJson"/>
<class name="TestEntity" apiProxy="myProxy">
<DateTime name="date"/>
<int name="justInt"/>
</class>
</spec>
copied to clipboard
As a result methods factory TestEntity.fromJson(Map<String, dynamic> json) and
Map<String, dynamic> toJson() will be generated for class TestEntity.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.