# This file is part of NIT ( http://www.nitlanguage.org ). # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Example of an ad hoc serializer that is tailored to transform business specific objects into customized representation. # # In the following, we expose 3 business classes, `E`, `A` and `B`, and a specific business serializer `RestrictedSerializer`. # The principle is that the custom serialization logic in enclosed into the `RestrictedSerializer` and that the # standard serializer is unchanged. # # The additional behaviors exposed are: # # * replace a full business object (see `E::id`): # instead of serializing an attribute, the custom serializer uses a different representation. # * inject a phantom attribute (see `E::phantom`): # when serializing, the custom serializer injects new attributes. # * hide a normally serialized attribute (see `E::semi_private`): # when serializing, the custom serializer hides some specific attributes. # # The advantage of the approach is that it is done programmatically so can be adapted to real complex use cases. # Basically, this is half-way between the full automatic serialization and the full manual serialisation. module custom_serialization is example import serialization import json::serialization_write # The root class of the business objects. # This factorizes most services and domain knowledge used by the `RestrictedSerializer` # # In real enterprise-level code, the various specific behaviors can be specified in more semantic classifiers. abstract class E serialize # The semantic business identifier. # # With the `RestrictedSerializer`, references to `E` objects will be replaced with `id`-based information. # This avoid to duplicate or enlarge the information cross-call wise. # # A future API/REST call can then request the _missing_ object from its identifier. var id: String # A phantom attribute to be serialized by the custom `RestrictedSerializer`. # # This can be used to inject constant or computed information that make little sense to have as a genuine attribute in # the Nit model. fun phantom: String do return "So Much Fun" # An attribute not to be serialized by the custom `RestrictedSerializer`. # e.g. we want it on the DB but not in API/REST JSON messages # # Note that the annotation `noserialize` hides the attribute for all serializers. # To hide the attribute only in the `RestrictedSerializer`, it will have to actively ignore it. var semi_private = "secret" # Test method that serializes `self` and prints with the standard JsonSerializer fun ser_json do var w = new StringWriter var js = new JsonSerializer(w) js.plain_json = true js.serialize(self) print w end # Test method that serializes `self` and prints with the custom RestrictedJsonSerializer. fun ser_json2 do var w = new StringWriter var js = new RestrictedJsonSerializer(w) js.plain_json = true js.serialize(self) print w end end # Extends Serializer and adds specific business behaviors when dealing with business objects. # # As with standard Nit, additional level of customization can be achieved by adding more double-dispatching :) # We can thus choose to locate the specific behavior in the serializer, or the serializees. class RestrictedSerializer super Serializer # This method is called to generate the attributes of a serialized representation redef fun serialize_core(value) do super if value isa E then # Inject additional special domain-specific information serialize_attribute("more-data", value.phantom) end end # This method is called when trying to serialize a specific attribute redef fun serialize_attribute(name, value) do var recv = current_object if recv isa E then # do not serialize `E::semi_private` if name == "semi_private" then return end if value isa E then # Do not serialize references to `E`. # Just use a domain-specific value that make sense in the business logic. serialize_attribute(name, "ID:" + value.id) return end super end end # Extends JsonSerializer and adds specific business behaviors when dealing with business objects. class RestrictedJsonSerializer super JsonSerializer super RestrictedSerializer end # A business object, with an integer information class A super E serialize # A business information var i: Int end # A business object associated with an `A`. class B super E serialize # A business association var a: A end # The business data to serialize var a = new A("a", 1) var b = new B("b", a) a.ser_json a.ser_json2 b.ser_json b.ser_json2