083bcfb254155e4443fda7e1bc554975363efb55
[nit.git] / lib / serialization / examples / custom_serialization.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Example of an ad hoc serializer that is tailored to transform business specific objects into customized representation.
16 #
17 # In the following, we expose 3 business classes, `E`, `A` and `B`, and a specific business serializer `RestrictedSerializer`.
18 # The principle is that the custom serialization logic in enclosed into the `RestrictedSerializer` and that the
19 # standard serializer is unchanged.
20 #
21 # The additional behaviors exposed are:
22 #
23 # * replace a full business object (see `E::id`):
24 # instead of serializing an attribute, the custom serializer uses a different representation.
25 # * inject a phantom attribute (see `E::phantom`):
26 # when serializing, the custom serializer injects new attributes.
27 # * hide a normally serialized attribute (see `E::semi_private`):
28 # when serializing, the custom serializer hides some specific attributes.
29 #
30 # The advantage of the approach is that it is done programmatically so can be adapted to real complex use cases.
31 # Basically, this is half-way between the full automatic serialization and the full manual serialisation.
32 module custom_serialization
33
34 import serialization
35 import json::serialization_write
36
37 # The root class of the business objects.
38 # This factorizes most services and domain knowledge used by the `RestrictedSerializer`
39 #
40 # In real enterprise-level code, the various specific behaviors can be specified in more semantic classifiers.
41 abstract class E
42 serialize
43
44 # The semantic business identifier.
45 #
46 # With the `RestrictedSerializer`, references to `E` objects will be replaced with `id`-based information.
47 # This avoid to duplicate or enlarge the information cross-call wise.
48 #
49 # A future API/REST call can then request the _missing_ object from its identifier.
50 var id: String
51
52 # A phantom attribute to be serialized by the custom `RestrictedSerializer`.
53 #
54 # This can be used to inject constant or computed information that make little sense to have as a genuine attribute in
55 # the Nit model.
56 fun phantom: String do return "So Much Fun"
57
58 # An attribute not to be serialized by the custom `RestrictedSerializer`.
59 # e.g. we want it on the DB but not in API/REST JSON messages
60 #
61 # Note that the annotation `noserialize` hides the attribute for all serializers.
62 # To hide the attribute only in the `RestrictedSerializer`, it will have to actively ignore it.
63 var semi_private = "secret"
64
65 # Test method that serializes `self` and prints with the standard JsonSerializer
66 fun ser_json
67 do
68 var w = new StringWriter
69 var js = new JsonSerializer(w)
70 js.plain_json = true
71 js.serialize(self)
72 print w
73 end
74
75 # Test method that serializes `self` and prints with the custom RestrictedJsonSerializer.
76 fun ser_json2
77 do
78 var w = new StringWriter
79 var js = new RestrictedJsonSerializer(w)
80 js.plain_json = true
81 js.serialize(self)
82 print w
83 end
84 end
85
86 # Extends Serializer and adds specific business behaviors when dealing with business objects.
87 #
88 # As with standard Nit, additional level of customization can be achieved by adding more double-dispatching :)
89 # We can thus choose to locate the specific behavior in the serializer, or the serializees.
90 class RestrictedSerializer
91 super Serializer
92
93 # This method is called to generate the attributes of a serialized representation
94 redef fun serialize_core(value)
95 do
96 super
97
98 if value isa E then
99 # Inject additional special domain-specific information
100 serialize_attribute("more-data", value.phantom)
101 end
102 end
103
104 # This method is called when trying to serialize a specific attribute
105 redef fun serialize_attribute(name, value)
106 do
107 var recv = current_object
108 if recv isa E then
109 # do not serialize `E::semi_private`
110 if name == "semi_private" then return
111 end
112
113 if value isa E then
114 # Do not serialize references to `E`.
115 # Just use a domain-specific value that make sense in the business logic.
116 serialize_attribute(name, "ID:" + value.id)
117 return
118 end
119
120 super
121 end
122 end
123
124 # Extends JsonSerializer and adds specific business behaviors when dealing with business objects.
125 class RestrictedJsonSerializer
126 super JsonSerializer
127 super RestrictedSerializer
128 end
129
130 # A business object, with an integer information
131 class A
132 super E
133 serialize
134
135 # A business information
136 var i: Int
137 end
138
139 # A business object associated with an `A`.
140 class B
141 super E
142 serialize
143
144 # A business association
145 var a: A
146 end
147
148 # The business data to serialize
149 var a = new A("a", 1)
150 var b = new B("b", a)
151
152 a.ser_json
153 a.ser_json2
154 b.ser_json
155 b.ser_json2