Серіалізація JSON у Rails: детальне керівництво

12 хв. читання

З того часу, як JavaScript став основним інструментом для роботи в вебі, серіалізація JSON стала важливою частиною багатьох веб-застосунків. У статті я поясню, що таке JSON-серіалізація, та чому вона необхідна у Rails-застосунках, а також як використовувати наявні бібліотеки для серіалізації JSON лаконічно та ефективно. Почну з пояснень, потім обговоримо деякі з найбільш популярних рішень і, нарешті, змоделюємо впровадження JSON-серіалізації, використовуючи оптимальні рішення в Ruby on Rails застосунках.

Для повного розуміння наведеного у статті коду будуть корисними базові знання Ruby.

Що таке JSON-серіалізація?

JSON (JavaScript Object Notation) — формат обміну даних, що представляє об'єкти у вигляді рядків. Такий формат даних може легко передаватися між серверами. Серіалізація — процес, що перетворює об'єкт у рядок, який передається.

Навіщо JSON-серіалізація у Rails-застосунках?

Якщо у вашому Rails-застосунку є API, що використовує JSON, його можна застосовувати з популярними JavaScript фреймворками так само, як і будь-який застосунок, що підтримує JSON.

Два етапи JSON-серіалізації

Процес серіалізації формату JSON складається з двох етапів: підготовка даних та безпосередньо перетворення у формат JSON.

Підготовка даних

Підготовка даних являє собою перетворення об'єктів Ruby у hash-таблицю. Якщо у вас є модель класу Person, що містить email, та ім'я, то атрибути об'єктів, готові для серіалізації, будуть виглядати так:

{"email" => "john@doe.com", "name" => "John Doe"}

Зауважте, що в Rails існує можливість викликати метод #to_json для екземпляра. У результаті буде повернено hash-таблицю з усіма атрибутами. Доволі часто потреба в атрибутах моделі відсутня, тому важливо підготувати hash-таблицю лише з тими даними, що будуть необхідні. Подібна поведінка — частина хорошого тону підготовки даних.

Нижче наведено повний перелік правил хорошого тону, яких необхідно дотримуватись при підготовці даних:

  • Обирайте лише ті атрибути, які справді необхідні у JSON-відповіді. Найпростіше реалізувати це у Rails шляхом виклику #to_json для екземпляра моделі класу та передати тільки :option, що зберігає масив назв атрибутів, які ви дійсно потребуєте: person.to_json(only: [:email, :name])

  • Розгляньте можливість скорочення змісту. Якщо необхідно відобразити багато записів у списку, можна скоротити опис або значення, щоб помістити більше записів на сторінці. Логіка такого скорочення найкраще виконується на серверній частині, тому фронт-енд відповідає лише за відображення інформації.

  • Переконайтеся в тому, що назви атрибутів інформативні. Якщо ви працюєте з готовим застосунком та маєте справу з погано названими атрибутами, подбайте про використання більш описових імен у JSON-відповідях. Таким чином легше визначити, що містить поле, подивившись лише на його назву.

  • Вибирайте асоціації з розумом. Подумайте, чи дійсно атрибут є необхідним перед додаванням асоціації. Завантаження непотрібних асоціацій значно уповільнює процес підготовки даних.

Найбільш відомі інструменти для підготовки даних

Існує багато пакетів, що застосовуються при JSON-серіалізації у Ruby on Rails застосунках. Їх API розроблено аналогічно; однак, існують деякі важливі відмінності. Я огляну найбільш популярні інструменти підготовки даних та порівняю їх показники, щоб визначити найкращий.

З метою тестування я буду використовувати дві моделі класів: Post та Comment.

Як повторити

Якщо хочете самостійно протестувати приклади коду, наведені нижче, можете створити новий Rails-проект, використовуючи наступний код:

rails new jsontest
cd jsontest
bundle exec rake db:create
bundle exec rails g model post title:string content:text published:boolean
bundle exec rails g model comment author:string body:text post_id:integer
bundle exec rake db:migrate

# app/models/post.rb

class Post < ActiveRecord::Base
  has_many :comments
end

# app/models/comment.rb

class Comment < ActiveRecord::Base
  belongs_to :post
end

# Завантажимо тестові дані — bundle exec rails c

post = Post.create!(title: "Post", content: "content", published: true)
Comment.create!(post: post, author: "Author", body: "Comment")

** Active Model Serializers**

Реалізація ActiveModel::Serializer є дуже популярною, незважаючи на те, що остаточна версія ще не була випущена. Поточною стабільною версією є 0.10, але й вона проходить етап усунення помилок.

Додаємо пакет з бібліотекою до нашого gem-файлу:

gem 'active_model_serializers', '~> 0.10.0'

Створимо серіалізатори для наших моделей Post та Comment:

rails g serializer post
rails g serializer comment

Серіалізатори створено у директорії app/serializers. Перед їх використанням необхідно повідомити Rails, що вони повинні бути доступні автоматично. Для цього відредагуйте файл config/application.rb , додавши наступний рядок:

config.autoload_paths += ["#{config.root}/app/serializers"]

Переконайтеся, що ваші серіалізатори мають такий зміст:

# app/serializers/post_serializer.rb

class PostSerializer < ActiveModel::Serializer
  has_many :comments

  attributes :id, :title, :content
end

# app/serializers/comment_serializer.rb

class CommentSerializer < ActiveModel::Serializer
  attributes :id, :body, :author
end

Протестуємо їх:

post = Post.joins(:comments).first
PostSerializer.new(post).as_json # => {:id=>12, :title=>"Post", :content=>"content", :comments=>[{:id=>201, :body=>"Comment", :author=>"Author"}]}

JSONAPI-RB

JSONAPI-RB — інтуїтивно зрозуміла бібліотека Ruby, що складається із чотирьох незалежних мікробібліотек:jsonapi-parser, jsonapi-renderer, jsonapi-serializable та jsonapi-deserializable.

Додамо пакет з бібліотекою до нашого Gemfile:

gem 'jsonapi-rails'

Оновлюємо серіалізатори, які збережено за шляхами app/serializers/post_serializer.rb та app/serializers/comment_serializer.rb:

# app/serializers/post_serializer.rb

class PostSerializer < JSONAPI::Serializable::Resource
  type 'posts'
  has_many :comments
  attributes :id, :title, :content
end

# app/serializers/comment_serializer.rb

class CommentSerializer < JSONAPI::Serializable::Resource
  type 'comments'

  attributes :id, :author, :body
end

Ми готові протестувати їх роботу:

post = Post.joins(:comments).first
renderer = JSONAPI::Serializable::Renderer.new
renderer.render(post, class: { Post: PostSerializer, Comment: CommentSerializer }, include: [:comments]) # => {:data=>{:id=>"113", :type=>:posts, :attributes=>{:id=>113,
:title=>"Post",
:content=>"content"},
:relationships=>{:comments=>{:data=>[{:type=>:comments, 
:id=>"2702"}]}}}, :included=>[{:id=>"2702", :type=>:comments, 
:attributes=>{:id=>2702, :author=>"Author", :body=>"Comment"}}]}

Fast JSON API

Блискавично швидкий JSON:API серіалізатор від Netflix. Додамо пакет з бібліотекою до нашого Gemfile: gem 'fast_jsonapi'

Наступним кроком буде оновлення наших серіалізаторів з метою використання Fast JSON API пакету:

# app/serializers/post_serializer.rb

class PostSerializer
  include FastJsonapi::ObjectSerializer
  attributes :title, :content
  has_many :comments
end

# app/serializers/comment_serializer.rb

class CommentSerializer
  include FastJsonapi::ObjectSerializer
  attributes :id, :body, :author
end

Наразі можемо підготувати дані для серіалізації:

post = Post.joins(:comments).first
PostSerializer.new(post, include: [:comments]).serializable_hash # => 
{:data=>{:id=>"12", :type=>:post, :attributes=>{:title=>"Post", 
:content=>"content"}, :relationships=>{:comments=>{:data=>[{:id=>"201", :type=>:comment}]}}}, :included=>[{:id=>"201", :type=>:comment, 
:attributes=>{:id=>201, :body=>"Comment", :author=>"Author"}}]}

RABL

RABL (Ruby API Builder Language) — система шаблонів у Ruby для генерації JSON.

За звичкою, спочатку додаємо пакет до Gemfile:

gem 'rabl'

Цього разу не визначаємо серіалізатор за шляхом app/serializers, натомість створюємо шаблон JSON. Додаємо нові файли:app/views/post.rabl та app/views/comment.rabl.

# app/views/post.rabl

object @job

attributes :id, :title, :content

child :comments do
 extends "comment"
end

# app/views/comment.rabl

object @comment

attributes :id, :author, :body

За замовчуванням пакет автоматично використовує створені шаблони у Rails контролерах при запиті JSON-відповіді. Однак, можливо також підготувати дані на рівні консолі:

post = Post.joins(:comments).first
Rabl.render(post, 'post', :view_path => 'app/views', :format => :hash) # 
=> {:id=>12, :title=>"Post", :content=>"content", :comments=>[{:id=>201, :author=>"Author", :body=>"Comment"}]}

JSON API Serializers

JSONAPI::Serializers — проста бібліотека для серіалізації об'єктів Ruby.

Додайте наступний рядок до Gemfile:

gem 'jsonapi-serializers'

Далі оновлюємо вже створені серіалізатори:

# app/serializers/post_serializer.rb

class PostSerializer
  include JSONAPI::Serializer

  attribute :id
  attribute :title
  attribute :content

  has_many :comments
end

# app/serializers/comment_serializer.rb

class CommentSerializer
  include JSONAPI::Serializer

  attribute :id
  attribute :author
  attribute :body
end

Тепер можемо генерувати дані з JSON-відповіді:

post = Post.joins(:comments).first
JSONAPI::Serializer.serialize(post, include: ['comments']) # =>
{"data"=>{"type"=>"posts", "id"=>"12", "attributes"=>{"id"=>12,
"title"=>"Post", "content"=>"content"}, "links"=>{"self"=>"/posts/12"}, 
"relationships"=>{"comments"=>{"links"=>{"self"=>"/posts/12/relationships/comments", "related"=>"/posts/12/comments"}, 
"data"=>[{"type"=>"comments", "id"=>"201"}]}}}, 
"included"=>[{"type"=>"comments", "id"=>"201", 
"attributes"=>{"id"=>201, 
"author"=>"Author", "body"=>"Comment"}, 
"links"=>{"self"=>"/comments/201"}}]}

JBuilder

JBuilder — пакет, що надає прості DSL (Domain Specific Languages) для оголошення структур JSON. Зараз нема потреби оновлювати наш Gemfile, тому що пакет вже встановлено за замовчуванням у Rails. Зазвичай структури зберігаються у файлі з розширенням json.jbuilder. Тож створимо файл app/views/post2.json.jbuilder та додамо туди наступний код:

json.id post.id
json.title post.title
json.content post.content

json.comments(post.comments) do |comment|
  json.id comment.id
  json.author comment.author
  json.body comment.body
end

Можна завантажити шаблон та генерувати JSON або не користуватися шаблоном:

post = Post.joins(:comments).first

# With template

renderer = ApplicationController.new
renderer.render_to_string('/post2', locals: {post: post}) # => "{\\"id\\":114,\\"title\\":\\"Title 0\\",\\"content\\":\\"Content 0\\",\\"comments\\":[{\\"id\\":2727,\\"author\\":\\"Author 24\\",\\"body\\":\\"Comment 24\\"}]}"

# Without template

def jbuild(*args, &block)
  Jbuilder.new(*args, &block).attributes!
end

result = jbuild do |json|
  json.id post.id
  json.title post.title
  json.content post.content

  json.comments(post.comments) do |comment|
    json.id comment.id
    json.author comment.author
    json.body comment.body
  end
end

result # => {"id"=>12, "title"=>"Post", "content"=>"content", "comments"=>[{"id"=>201, "author"=>"Author", "body"=>"Comment"}]}

Серіалізація

Тепер, коли ми закінчили із тестуванням різних пакетів Ruby, переходимо до наступного етапу серіалізації JSON: трансформації хеш-таблиць у формат JSON. Найбільш популярні рішення:

Oj

Oj — швидкий JSON-парсер та об'єктний маршалер (marshaller) Ruby. Для його встановлення додаємо один рядок до Gemfile:

gem 'oj'

Можемо спробувати перетворити хеш-таблицю на формат JSON:

hash = {name: "John Doe", email: "john@doe.com"}
Oj.dump(hash) # => "{\\"name\\":\\"John Doe\\",\\"email\\":\\"john@doe.com\\"}"

JSON

Бібліотека JSON є стандартною для здійснення серіалізації в Ruby, тому можемо використовувати її прямо в нашій консолі, без додавання стороннього коду:

hash = {name: "John Doe", email: "john@doe.com"}
JSON.generate(hash) # => "{\\"name\\":\\"John Doe\\",\\"email\\":\\"john@doe.com\\"}"

Yajl

Yajl — потокова JSON-бібліотека синтаксичного аналізу та кодування для Ruby.

Перед використанням треба додати пакет:

gem 'yajl-ruby'

Наразі можемо серіалізувати приклад хешу:

require 'yajl'

hash = {name: "John Doe", email: "john@doe.com"}
Yajl::Encoder.encode(hash) # => "{\\"name\\":\\"John Doe\\",\\"email\\":\\"john@doe.com\\"}"

Найкраще рішення для JSON-реалізації у Rails

Ми оглянули найбільш популярні інструменти для підготовки інформації та серіалізації JSON. Час обрати найбільш продуктивну пару. Я порівняю швидкість підготовки даних та JSON-серіалізації, використовуючи код із прикладів вище.

Для тесту я створив 100 об'єктів Post, кожен з яких матиме по 25 коментарів:

100.times do |i|
  pos = Post.create!(title: "Title #{i}", content: "Content #{i}")
  25.times do |i2|
    Comment.create!(post: post, author: "Author #{i2}", body: "Comment #{i2}")
  end
end

Щоб порівняти час підготовки даних, необхідний для кожного інструменту, я використаю модуль Ruby Benchmark.

Інструменти підготовки даних

Нижче наведено рейтинг рішень від найшвидшого до найповільнішого:

Назва Час (секунди)
FAST JSON API 0.121921
Active Model Serializers 0.154672
JSONAPI-RB 0.246346
JSON API Serializers 0.262120
RABL 0.853417
JBuilder 2.559193

Найшвидшим виявився Fast JSON API, а найповільнішим — JBuilder та RABL.

На щастя, FAST JSON API досить зрозумілий на інтуїтивному рівні та не потребує додаткових налаштувань. Все, що необхідно для початку роботи з ним — визначити атрибути та асоціації для даної моделі.

Код, що тестувався, можна переглянути тут.

Рішення для серіалізації JSON

Назва Час (секунди)
Oj 0.007225
Yajl 0.014289
JSON 0.036572

Найшвидшою виявилася бібліотека Oj, що у декілька разів спритніша за опонентів. Тож Oj — безумовний лідер.

Впровадження швидкої JSON-серіалізації до Ruby on Rails застосунку

В результаті тестування перемогла пара інструментів: Fast JSON API та Oj. Час застосувати обидва рішення у застосунку Ruby on Rails, щоб продемонструвати показову JSON-серіалізацію.

Почнемо з додавання необхідних пакетів до нашого Gemfile:

gem 'oj'
gem 'fast_jsonapi'

Створення контролеру

Спочатку було створено гілку об'єктів Post із коментарями. Час дійти кінцевої точки — перетворити інформацію у формат JSON. Ддодамо новий клас контролеру app/controllers/posts_controller.rb з наступним змістом:

class PostsController < ApplicationController
  def index
    posts = Post.joins(:comments)
  end
end

Створення серіалізатору

Для того, щоб провести серіалізацію об'єктів, необхідно визначити серіалізатор з необхідними для перетворення атрибутами. Наведений код виглядає досить знайомо, тому що ми вже використовували його при демонстрації пакетів:

# app/serializers/post_serializer.rb

class PostSerializer
  include FastJsonapi::ObjectSerializer
  attributes :title, :content
  has_many :comments
end

# app/serializers/comment_serializer.rb

class CommentSerializer
  include JSONAPI::Serializer

  attribute :id
  attribute :author
  attribute :body
end

Використання OJ

Відтоді, як пакет fast_jsonapi автоматично включає Oj, немає потреби явно вказувати застосунку Rails включати пакет.

Підготовка відповіді у форматі JSON

Наші серіалізатори підготовлено, тому залишилося відобразити повідомлення у форматі JSON. Відредагуємо попередньо створений контролер:

class PostsController < ApplicationControlle
  def index
    posts = Post.joins(:comments)
    render json: PostSerializer.new(posts).serialized_json
  end
end

Сповістимо Rails, що ми хочемо отримати до нього доступ, відредагувавши файл config/routes.rb:

resources :posts, only: [:index]

Запускаємо команду rails s та отримуємо доступ до згенерованої інформації у форматі JSON, використовуючи URL.

Ключові моменти

У статті ми:

  • Порівняли шість найбільш популярних інструментів підготовки інформації для JSON-серіалізації та обрали найкращий.
  • Порівняли три найбільш популярні інструменти для JSON-серіалізації та виявили найкращий.
  • Реалізували механізм швидкої JSON-серіалізації у застосунку Ruby on Rails, використовуючі найшвидші з доступних рішень.

Як наслідок, отримали широкий та надзвичайно швидкий механізм. Такий підхід розміщує логіку серіалізації на рівні моделі й вимагає від нас прямої вказівки, який серіалізатор необхідно використовувати. Важливо, що нема потреби у додаткових налаштуваннях, щоб почати роботу. Використання базового коду достатньо для забезпечення завершеної JSON-відповіді у контролерах.

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 1.7K
Приєднався: 1 рік тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід