Skip to content

Repository files navigation

JSerializer

Build StatusCode Climate

JSerializer is a JSON Serializer for Ruby objects. It is designed to be a drop-in replacement of Active Model Serializer (target version: 0.8) with better performance.

JSerializer does not rely on Rails or Active Model or Active Support, which makes it easier to be integrated into general Ruby projects.

Installation

Add this line to your application's Gemfile:

gem'jserializer'

And then execute:

$ bundle

Or install it yourself as:

$ gem install jserializer

Usage

Define a Model

Person=Struct.new(:id,:first_name,:last_name,:age,:gender,:country)

Create a Serializer

classPersonSerializer < Jserializer::Baseroot:userattributes:full_name,:age,:genderattribute:country,key: :country_codedeffull_name"#{object.first_name}#{object.last_name}"enddefgenderobject.gender == 'm' ? 'Male' : 'Female'enddefinclude_age?object.age >= 18endend

Generate JSON

person=Person.new(1,'John','Doe',16,'m','US')serializer=PersonSerializer.new(person)# generates a Hash without root key# => {:full_name=>"John Doe", :gender=>"Male", :country_code=>"US"}serializer.serializable_hash# generates a Hash with root key# => {:user=>{:full_name=>"John Doe", :gender=>"Male", :country_code=>"US"}}serializer.as_json# generates JSON => {"user":{"full_name":"John Doe","gender":"Male","country_code":"US"}}serializer.to_json

Generate JSON Collection

persons=2.times.map{|i| Person.new(i,'Person',"#{i}",17 + i,'m','US')}serializer=PersonSerializer.new(persons,is_collection: true)serializer.to_json

You will get:

{
"user":[
{
"full_name":"Person 0",
"gender":"Male",
"country_code":"US"
},
{
"full_name":"Person 1",
"age":18,
"gender":"Male",
"country_code":"US"
}
]
}

Bring Your Own JSON Encoder

Our to_json method uses standard JSON module to generate JSON string. There are many JSON encode backend there, and they offer different customization options. Besides, you can use MultiJson to switch between different backends.

You are welcome to bring your own solution here. To do that, simply override the to_json method

classApplicationSerializer < Jserializer::Basedefto_json(*)# use ActiveSupport in Rails as a delegatorActiveSupport::JSON.encode(as_json)# use oj:# Oj.dump(as_json, mode: :compat, use_to_json: true)endend

Then the rest of your serializers can inherit from ApplicationSerializer and start to use your preferred encoder.

Serializer Class Definition Options

MethodOptionsDescription
rootN/ASet the root key of the generated JSON
attributesN/ADefine a list of fields separated by , to be exposed from a Ruby object
attribute:key - The name in the JSON outputSimilar to attributes but for one field
has_many:serializer
:key
:embed
:embed_key
Include a collection of objects with has many association
has_oneSame as has_manyInclude a object with has one association
embed:ids
:objects
Determine if only include IDs of the associations

Example

This example shows you where to apply the above methods

classPostSerializer < Jserializer::Baseroot:articleembed:idsattributes:id,:title,:contentattribute:writer,key: :written_byhas_many:comments,serializer: CommentSerializer,embed: :objectshas_one:author,serializer: AuthorSerializer,embed_key: :idend

For associations, Jserializer uses the following ways to retrieve data:

TypeMethodExample
has_manycollection_singular_idsposts => post_ids
has_oneassociation.idaccount => account.id

Initialization Options for Serializer Instance

OptionsDescription
rootSet the root key of the generated JSON, set it to false to disable
metaMeta information to be included in the JSON output
meta_keyThe key name of the meta information, the default is :meta
is_collectionWhether the given object is a collection or single object
onlyAn array of attributes to be included in the JSON output
exceptAn array of attributes to be excluded in the JSON output
current_userUse for determine the authorization scope

Example

PostSerializer.new(posts,root: :post,meta: {page: 1,total: 100},is_collection: true,only: [:title,:content])

You can enable/disable root when initializing a serializer instance:

PostSerializer.new(post,root: false)

Or when calling as_json method:

# here the root option only accept a boolean value# you cannot rename root at this pointPostSerializer.new(post).as_json(root: false)

You can always get the Hash representation without root and meta information by calling serializable_hash

PostSerializer.new(post).serializable_hash

Collection

The active_serializer_model gem includes the ArraySerializer class to handle collections. There are a lot of magics happening underneath when you pass a collection object into render json: @xxx, to allow ArraySerializer gets triggered automatically.

Unlike active_serializer_model, there is no separate serializer class for array. To serialize a collection, you need to set is_collection: true when initializing a new serializer

serializer=PostSerializer.new(posts,is_collection: true)serializer.serializable_hash# or serializer.as_json to include root

You can also call serializable_collection method directly which will ignore the is_collection option

serializer=PostSerializer.new(posts)serializer.serializable_collection

Compatibility & Migration

Currently, this gem is not compatible with active_serializer_model if you:

  • have include_xxx? as private method
  • override the instance method attributes
  • override any internal method _xxx (e.g. _serializable_array)
  • expect serializer to automatically include a root for you
  • expect serializer figures out if the object is a collection automatically

Since we try to reuse serializer instances to avoid unnecessary object creations, make sure there is no things like ||= in the serializer class. Or you can override reset method to clean things out

classMySerializer < Jserializer::Base
... ...
defreset(object)@my_cached_stuff=nil
... ...
superend

active_model_serializer method

This gem will try to find and use the serializer class defined by active_model_serializer method in a model, if you don't specify serializer explicitly

classPost < ActiveRecord::Basedefactive_model_serializerMyPostSerializerendend

Use in Rails Action Controller

Active Model Serializer overrides render :json in ActionController::Serialization, which is convenient. But it touches Rails internal methods which could bring compatibility issues when upgrading Rails.

This gem does not provide such feature, but you can easily achieve it in application layer, for example, create a wrapper method for render:

classApplicationController < ActionController::Base# ... ...defrender_json(resource,options={})ifoptions.key?(:serializer)serializer=options.delete(:serializer)elsifoptions.key?(:each_serializer)serializer=options.delete(:each_serializer)options[:is_collection]=trueendif !serializer && resource.respond_to?(:active_model_serializer)serializer=resource.active_model_serializerendifserializeroptions[:scope]=current_useroptions[:json]=serializer.new(resource,options)elseoptions[:json]=resourceendrenderoptionsendend

Then you can use this render_json method whenever you need to call render json: resource ... in your controllers. And this is probably a good way to migrate gradually.

Caching

This gem does not plan to implement the cache feature.

Benchmark

See here

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/distil/jserializer.

About

A JSON Serializer for Ruby Objects

Topics

Resources

Stars

14 stars

Watchers

27 watching

Forks

Releases

Packages

Used by

Contributors

Languages