serializer v0.2.0
Serializer
Serializer is a simple JSON serialization library for your object structure. Unlike core JSON module's functionality this library only covers serializing objects to JSON without parsing data back. At the same time it provides some free space for maneuvers, precise and flexible configuration WHAT, HOW and WHEN should be rendered.
Serializer::Base only ~11% slower than JSON::Serializable
Serializer 646.00k ( 1.55µs) (± 2.52%) 2.77kB/op 1.11× slower
JSON::Serializable 719.74k ( 1.39µs) (± 2.39%) 1.3kB/op fastest
and at the same time provides next functionality:
- conditional rendering at schema definition stage
- excluding specific fields at invocation stage
- separation fields from relations
- deep relation specification (to be rendered) at invocation stage
- inheritance
- optional meta data (can be specified at both definition and invocation stages).
Installation
-
Add the dependency to your
shard.yml:dependencies: serializer: github: imdrasil/serializer -
Run
shards install
Usage
Let's assume we have next resources relationship
class Parent
property name, title,
children : Array(Child),
friends : Array(Child)
def initialize(@name = "test", @title = "asd", @children = [] of Child, @friends = [] of Child)
end
end
class Child
property age : Int32, dipper : Child?, address : Address?
def initialize(@age, @dipper = nil, @address = nil)
end
def some_sub_relation; end
end
class Address
property street
def initialize(@street = "some street")
end
end
To be able to serialize data we need to define serializers for each resource:
class AddressSerializer < Serializer::Base(Address)
attributes :street
end
class ChildSerializer < Serializer::Base(Child)
attribute :age
has_one :some_sub_relation, ChildSerializer
has_one :address, AddressSerializer
has_one :dipper, ChildSerializer
end
class ModelSerializer < Serializer::Base(Model)
attribute :name
attribute :title, :Title, if: :test_title
attribute :own_field
has_many :children, ChildSerializer
has_many :friends, ChildSerializer
def test_title(object, options)
options.nil? || !options[:test]?
end
def own_field
12
end
end
Attributes
To specify what should be serialized attributes and attribute macros are used. attributes allows to pass a list of attribute names which maps one-to-one with JSON keys
class PostSerializer
attributes :title, body
end
Above serializer will produce next output {"title": "Some title", "body": "Post body"}. You can precisely configure every field using attribute macro. It allows to specify key name to be used in JSON and if predicate method name to be used to check whether field should be serialized.
class ModelSerializer < Serializer::Base(Model)
attribute :title, :Title, if: :test_title
def test_title(object, options)
options.nil? || !options[:test]?
end
end
Above serializer will produce next output {"Title": "Some title"} if serializer has got options without test set to true.
If serializer has a method with the same name as specified field - it is used.
class ModelSerializer < Serializer::Base(Model)
attribute :name
def name
"StaticName"
end
end
Relations
If resource has underlying resources to serialize they can be specified with has_one, belongs_to and has_many macro methods that describes relation type between them (one-to-one, one-to-any and one-to-many).
class ModelSerializer < Serializer::Base(Model)
has_many :friends, ChildSerializer
end
They also accepts key option. There is no if support because associations by default isn't rendered.
Meta
Resource meta data can be defined at it's level - overriding .meta method.
class ModelSerializer < Serializer::Base(Model)
def self.meta(options)
{
:page => options[:page]
}
end
end
Method return value should be Hash(Symbol, JSON::Any::Type | Int32). Also any additional meta attributes may be defined at serialization moment (calling #serialize method).
Inheritance
If you have complicated domain object relation structure - you can easily present serialization logic using inheritance:
class ModelSerializer < Serializer::Base(Model)
attribute :name
end
class InheritedSerializer < ModelSerializer
attribute :inherited_field
def inherited_field
1.23
end
end
Rendering
To render resource create an instance of required serializer and call #serialize:
ModelSerializer.new(model).serialize
It accepts several optional arguments:
except- array of fields that should not be serialized;includes- relations that should be included into serialized string;opts- options that will be passed to if predicate methods and.meta;meta- meta attributes to be added under"meta"key at root level; it is merged into default meta attributes returned by.meta.
ModelSerializer.new(model).serialize(
except: [:own_field],
includes: {
:children => [:some_sub_relation],
:friends => { :address => nil, :dipper => [:some_sub_relation] }
},
meta: { :page => 0 }
)
includes should be array or hash (any levels deep) which elements presents relation names to be serialized. nil value may be used in hashes as a value to define that nothing additional should be serialized for a relation named by corresponding key.
Example above results in:
{
"data":{
"name":"test",
"Title":"asd",
"children":[],
"friends":[
{
"age":60,
"address":{
"street":"some street"
},
"dipper":{
"age":20,
"some_sub_relation":null
}
}
]
},
"meta":{
"page":0
}
}
This is pretty JSON version - actual result contains no spaces and newlines.
Root key
Serialized JSON root level includes data key (and optional meta key). It can be renamed to anything by defining .root_key
class ModelSerializer < Serializer::Base(Model)
def self.root_key
"model"
end
attribute :name
end
For API details see documentation.
Contributing
- Fork it (https://github.com/imdrasil/serializer/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Contributors
- Roman Kalnytskyi - creator and maintainer
serializer
- 10
- 2
- 0
- 1
- 1
- almost 4 years ago
- August 22, 2019
MIT License
Tue, 28 Oct 2025 10:45:37 GMT