oj_serializers 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,29 +6,33 @@ Oj Serializers
6
6
  <a href="https://codeclimate.com/github/ElMassimo/oj_serializers"><img alt="Maintainability" src="https://codeclimate.com/github/ElMassimo/oj_serializers/badges/gpa.svg"/></a>
7
7
  <a href="https://codeclimate.com/github/ElMassimo/oj_serializers"><img alt="Test Coverage" src="https://codeclimate.com/github/ElMassimo/oj_serializers/badges/coverage.svg"/></a>
8
8
  <a href="https://rubygems.org/gems/oj_serializers"><img alt="Gem Version" src="https://img.shields.io/gem/v/oj_serializers.svg?colorB=e9573f"/></a>
9
- <a href="https://github.com/ElMassimo/oj_serializers/blob/master/LICENSE.txt"><img alt="License" src="https://img.shields.io/badge/license-MIT-428F7E.svg"/></a>
9
+ <a href="https://github.com/ElMassimo/oj_serializers/blob/main/LICENSE.txt"><img alt="License" src="https://img.shields.io/badge/license-MIT-428F7E.svg"/></a>
10
10
  </p>
11
11
  </h1>
12
12
 
13
- JSON serializers for Ruby, built on top of the powerful [`oj`][oj] library.
13
+ Faster JSON serializers for Ruby, built on top of the powerful [`oj`][oj] library.
14
14
 
15
15
  [oj]: https://github.com/ohler55/oj
16
16
  [mongoid]: https://github.com/mongodb/mongoid
17
17
  [ams]: https://github.com/rails-api/active_model_serializers
18
18
  [jsonapi]: https://github.com/jsonapi-serializer/jsonapi-serializer
19
19
  [panko]: https://github.com/panko-serializer/panko_serializer
20
+ [blueprinter]: https://github.com/procore/blueprinter
20
21
  [benchmarks]: https://github.com/ElMassimo/oj_serializers/tree/master/benchmarks
21
- [raw_benchmarks]: https://github.com/ElMassimo/oj_serializers/blob/master/benchmarks/document_benchmark.rb
22
- [sugar]: https://github.com/ElMassimo/oj_serializers/blob/master/lib/oj_serializers/sugar.rb#L14
23
- [migration guide]: https://github.com/ElMassimo/oj_serializers/blob/master/MIGRATION_GUIDE.md
22
+ [raw_benchmarks]: https://github.com/ElMassimo/oj_serializers/blob/main/benchmarks/document_benchmark.rb
23
+ [sugar]: https://github.com/ElMassimo/oj_serializers/blob/main/lib/oj_serializers/sugar.rb#L14
24
+ [migration guide]: https://github.com/ElMassimo/oj_serializers/blob/main/MIGRATION_GUIDE.md
24
25
  [design]: https://github.com/ElMassimo/oj_serializers#design-
25
26
  [raw_json]: https://github.com/ohler55/oj/issues/542
26
27
  [trailing_commas]: https://maximomussini.com/posts/trailing-commas/
28
+ [render dsl]: https://github.com/ElMassimo/oj_serializers#render-dsl-
29
+ [sorbet]: https://sorbet.org/
30
+ [Discussion]: https://github.com/ElMassimo/oj_serializers/discussions
27
31
 
28
32
  ## Why? 🤔
29
33
 
30
- [`ActiveModel::Serializer`][ams] has a nice DSL, but it allocates many objects leading
31
- to memory bloat, time spent on GC, and lower performance.
34
+ [`ActiveModel::Serializer`][ams] has a nice DSL, but it allocates many objects
35
+ leading to memory bloat, time spent on GC, and lower performance.
32
36
 
33
37
  `Oj::Serializer` provides a similar API, with [better performance][benchmarks].
34
38
 
@@ -36,12 +40,11 @@ Learn more about [how this library achieves its performance][design].
36
40
 
37
41
  ## Features ⚡️
38
42
 
39
- - Declaration syntax similar to Active Model Serializers
40
- - Reduced memory allocation and [improved performance][benchmarks]
43
+ - Declaration syntax [similar to Active Model Serializers][migration guide]
44
+ - Reduced [memory allocation][benchmarks] and [improved performance][benchmarks]
41
45
  - Support for `has_one` and `has_many`, compose with `flat_one`
42
46
  - Useful development checks to avoid typos and mistakes
43
47
  - Integrates nicely with Rails controllers
44
- - Caching
45
48
 
46
49
  ## Installation 💿
47
50
 
@@ -58,13 +61,13 @@ And then run:
58
61
  ## Usage 🚀
59
62
 
60
63
  You can define a serializer by subclassing `Oj::Serializer`, and specify which
61
- attributes should be serialized to JSON.
64
+ attributes should be serialized.
62
65
 
63
66
  ```ruby
64
67
  class AlbumSerializer < Oj::Serializer
65
68
  attributes :name, :genres
66
69
 
67
- attribute \
70
+ attr
68
71
  def release
69
72
  album.release_date.strftime('%B %d, %Y')
70
73
  end
@@ -76,129 +79,84 @@ end
76
79
  <details>
77
80
  <summary>Example Output</summary>
78
81
 
79
- ```json
82
+ ```ruby
80
83
  {
81
- "name": "Abraxas",
82
- "genres": [
84
+ name: "Abraxas",
85
+ genres: [
83
86
  "Pyschodelic Rock",
84
87
  "Blues Rock",
85
88
  "Jazz Fusion",
86
- "Latin Rock"
89
+ "Latin Rock",
87
90
  ],
88
- "release": "September 23, 1970",
89
- "songs": [
91
+ release: "September 23, 1970",
92
+ songs: [
90
93
  {
91
- "track": 1,
92
- "name": "Sing Winds, Crying Beasts",
93
- "composers": [
94
- "Michael Carabello"
95
- ]
94
+ track: 1,
95
+ name: "Sing Winds, Crying Beasts",
96
+ composers: ["Michael Carabello"],
96
97
  },
97
98
  {
98
- "track": 2,
99
- "name": "Black Magic Woman / Gypsy Queen",
100
- "composers": [
101
- "Peter Green",
102
- "Gábor Szabó"
103
- ]
99
+ track: 2,
100
+ name: "Black Magic Woman / Gypsy Queen",
101
+ composers: ["Peter Green", "Gábor Szabó"],
104
102
  },
105
103
  {
106
- "track": 3,
107
- "name": "Oye como va",
108
- "composers": [
109
- "Tito Puente"
110
- ]
104
+ track: 3,
105
+ name: "Oye como va",
106
+ composers: ["Tito Puente"],
111
107
  },
112
108
  {
113
- "track": 4,
114
- "name": "Incident at Neshabur",
115
- "composers": [
116
- "Alberto Gianquinto",
117
- "Carlos Santana"
118
- ]
109
+ track: 4,
110
+ name: "Incident at Neshabur",
111
+ composers: ["Alberto Gianquinto", "Carlos Santana"],
119
112
  },
120
113
  {
121
- "track": 5,
122
- "name": "Se acabó",
123
- "composers": [
124
- "José Areas"
125
- ]
114
+ track: 5,
115
+ name: "Se acabó",
116
+ composers: ["José Areas"],
126
117
  },
127
118
  {
128
- "track": 6,
129
- "name": "Mother's Daughter",
130
- "composers": [
131
- "Gregg Rolie"
132
- ]
119
+ track: 6,
120
+ name: "Mother's Daughter",
121
+ composers: ["Gregg Rolie"],
133
122
  },
134
123
  {
135
- "track": 7,
136
- "name": "Samba pa ti",
137
- "composers": [
138
- "Santana"
139
- ]
124
+ track: 7,
125
+ name: "Samba pa ti",
126
+ composers: ["Santana"],
140
127
  },
141
128
  {
142
- "track": 8,
143
- "name": "Hope You're Feeling Better",
144
- "composers": [
145
- "Rolie"
146
- ]
129
+ track: 8,
130
+ name: "Hope You're Feeling Better",
131
+ composers: ["Rolie"],
147
132
  },
148
133
  {
149
- "track": 9,
150
- "name": "El Nicoya",
151
- "composers": [
152
- "Areas"
153
- ]
154
- }
155
- ]
134
+ track: 9,
135
+ name: "El Nicoya",
136
+ composers: ["Areas"],
137
+ },
138
+ ],
156
139
  }
157
140
  ```
158
141
  </details>
159
142
 
160
- <br/>
161
-
162
- To use the serializer, the recommended approach is:
143
+ You can then use your new serializer to render an object or collection:
163
144
 
164
145
  ```ruby
165
146
  class AlbumsController < ApplicationController
166
147
  def show
167
- album = Album.find(params[:id])
168
148
  render json: AlbumSerializer.one(album)
169
149
  end
170
150
 
171
151
  def index
172
- albums = Album.all
173
152
  render json: { albums: AlbumSerializer.many(albums) }
174
153
  end
175
154
  end
176
155
  ```
177
156
 
178
- If you are using Rails you can also use something closer to Active Model Serializers by adding [`sugar`][sugar]:
179
-
180
- ```ruby
181
- require 'oj_serializers/sugar'
182
-
183
- class AlbumsController < ApplicationController
184
- def show
185
- album = Album.find(params[:id])
186
- render json: album, serializer: AlbumSerializer
187
- end
188
-
189
- def index
190
- albums = Album.all
191
- render json: albums, each_serializer: AlbumSerializer, root: :albums
192
- end
193
- end
194
- ```
195
-
196
- It's recommended to create your own `BaseSerializer` class in order to easily
197
- add custom extensions, specially when migrating from `active_model_serializers`.
198
-
199
- ## Render DSL 🛠
157
+ ## Rendering 🖨
200
158
 
201
- In order to efficiently reuse the instances, serializers can't be instantiated directly. Use `one` and `many` to serialize objects or enumerables:
159
+ Use `one` to serialize objects, and `many` to serialize enumerables:
202
160
 
203
161
  ```ruby
204
162
  render json: {
@@ -207,173 +165,212 @@ render json: {
207
165
  }
208
166
  ```
209
167
 
210
- You can use these serializers inside arrays, hashes, or even inside `ActiveModel::Serializer` by using a method in the serializer.
211
-
212
- Follow [this discussion][raw_json] to find out more about [the `raw_json` extensions][raw_json] that made this high level of interoperability possible.
213
-
214
- ## Attributes DSL 🛠
168
+ Serializers can be rendered arrays, hashes, or even inside `ActiveModel::Serializer`
169
+ by using a method in the serializer, making it very easy to combine with other
170
+ libraries and migrate incrementally.
215
171
 
216
- Attributes methods can be used to define which model attributes should be serialized
217
- to JSON. Each method provides a different strategy to obtain the values to serialize.
172
+ You can use `render` as a shortcut for `one` and `many`, but it might be less readable:
218
173
 
219
- The internal design is simple and extensible, so creating new strategies requires very little code.
220
- Please open an issue if you need help 😃
174
+ ```ruby
175
+ render json: {
176
+ favorite_album: AlbumSerializer.render(album),
177
+ purchased_albums: AlbumSerializer.render(albums),
178
+ }
179
+ ```
221
180
 
222
- ### `attributes`
181
+ ## Attributes DSL 🪄
223
182
 
224
- Obtains the attribute value by calling a method in the object being serialized.
183
+ Specify which attributes should be rendered by calling a method in the object to serialize.
225
184
 
226
185
  ```ruby
227
186
  class PlayerSerializer < Oj::Serializer
228
- attributes :full_name
187
+ attributes :first_name, :last_name, :full_name
229
188
  end
230
189
  ```
231
190
 
232
- Have in mind that unlike Active Model Serializers, it will _not_ take into
233
- account methods defined in the serializer. Being explicit about where the
234
- attribute is coming from makes the serializers easier to understand and more
235
- maintainable.
236
-
237
- ### `serializer_attributes`
238
-
239
- Obtains the attribute value by calling a method defined in the serializer.
240
-
241
-
242
- You may call [`serializer_attributes`](https://github.com/ElMassimo/oj_serializers/blob/master/spec/support/serializers/song_serializer.rb#L13-L15) or use the `attribute` inline syntax:
191
+ You can serialize custom values by specifying that a method is an `attribute`:
243
192
 
244
193
  ```ruby
245
194
  class PlayerSerializer < Oj::Serializer
246
- attribute \
247
- def full_name
195
+ attribute :name do
248
196
  "#{player.first_name} #{player.last_name}"
249
197
  end
250
- end
251
- ```
252
-
253
- Instance methods can access the object by the serializer name without the
254
- `Serializer` suffix, `player` in the example above, or directly as `@object`.
255
-
256
- You can customize this by using [`object_as`](https://github.com/ElMassimo/oj_serializers#using-a-different-alias-for-the-internal-object).
257
198
 
258
- ### `ams_attributes` 🐌
199
+ # or
259
200
 
260
- Works like `attributes` in Active Model Serializers, by calling a method in the serializer if defined, or calling `read_attribute_for_serialization` in the model.
261
-
262
- ```ruby
263
- class AlbumSerializer < Oj::Serializer
264
- ams_attributes :name, :release
265
-
266
- def release
267
- album.release_date.strftime('%B %d, %Y')
201
+ attribute
202
+ def name
203
+ "#{player.first_name} #{player.last_name}"
268
204
  end
269
205
  end
270
206
  ```
271
207
 
272
- Should only be used when migrating from Active Model Serializers, as it's slower and can create confusion.
208
+ > **Note**
209
+ >
210
+ > In this example, `player` was inferred from `PlayerSerializer`.
211
+ >
212
+ > You can customize this by using [`object_as`](#using-a-different-alias-for-the-internal-object).
273
213
 
274
- Instead, use `attributes` for model methods, and the inline `attribute` for serializer attributes. Being explicit makes serializers easier to understand, and to maintain.
275
214
 
276
- Please refer to the [migration guide] for more information.
215
+ ### Associations 🔗
277
216
 
278
- ### `hash_attributes` 🚀
217
+ Use `has_one` to serialize individual objects, and `has_many` to serialize a collection.
279
218
 
280
- Very convenient when serializing Hash-like structures, this strategy uses the `[]` operator.
219
+ You must specificy which serializer to use with the `serializer` option.
281
220
 
282
221
  ```ruby
283
- class PersonSerializer < Oj::Serializer
284
- hash_attributes 'first_name', :last_name
222
+ class SongSerializer < Oj::Serializer
223
+ has_one :album, serializer: AlbumSerializer
224
+ has_many :composers, serializer: ComposerSerializer
285
225
  end
286
-
287
- PersonSerializer.one('first_name' => 'Mary', :middle_name => 'Jane', :last_name => 'Watson')
288
- # {"first_name":"Mary","last_name":"Watson"}
289
226
  ```
290
227
 
291
- ### `mongo_attributes` 🚀
292
-
293
- Reads data directly from `attributes` in a [Mongoid] document.
294
-
295
- By skipping type casting, coercion, and defaults, it [achieves the best performance][raw_benchmarks].
296
-
297
- Although there are some downsides, depending on how consistent your schema is,
298
- and which kind of consumer the API has, it can be really powerful.
228
+ Specify a different value for the association by providing a block:
299
229
 
300
230
  ```ruby
301
- class AlbumSerializer < Oj::Serializer
302
- mongo_attributes :id, :name
231
+ class SongSerializer < Oj::Serializer
232
+ has_one :album, serializer: AlbumSerializer do
233
+ Album.find_by(song_ids: song.id)
234
+ end
303
235
  end
304
236
  ```
305
237
 
306
- ## Associations DSL 🛠
238
+ In case you need to pass options, you can call the serializer manually:
307
239
 
308
- Use `has_one` to serialize individual objects, and `has_many` to serialize a collection.
240
+ ```ruby
241
+ class SongSerializer < Oj::Serializer
242
+ attribute :album do
243
+ AlbumSerializer.one(song.album, for_song: song)
244
+ end
245
+ end
246
+ ```
309
247
 
310
- The value for the association is obtained from a serializer method if defined, or by calling the method in the object being serialized.
248
+ ### Aliasing or renaming attributes ↔️
311
249
 
312
- You must specificy which serializer to use with the `serializer` option.
250
+ You can pass `as` when defining an attribute or association to serialize it
251
+ using a different key:
313
252
 
314
253
  ```ruby
315
254
  class SongSerializer < Oj::Serializer
316
- has_one :album, serializer: AlbumSerializer
317
- has_many :composers, serializer: ComposerSerializer
255
+ has_one :album, as: :first_release, serializer: AlbumSerializer
318
256
 
319
- # You can also compose serializers using `flat_one`.
320
- flat_one :song, serializer: SongMetadataSerializer
257
+ attributes title: {as: :name}
258
+
259
+ # or as a shortcut
260
+ attributes title: :name
321
261
  end
322
262
  ```
323
263
 
324
- The associations DSL is more concise and achieves better performance, so prefer to use it instead of manually definining attributes:
264
+ ### Conditional attributes
265
+
266
+ You can render attributes and associations conditionally by using `:if`.
325
267
 
326
268
  ```ruby
327
- class SongSerializer < SongMetadataSerializer
328
- attribute \
329
- def album
330
- AlbumSerializer.one(song.album)
331
- end
269
+ class PlayerSerializer < Oj::Serializer
270
+ attributes :first_name, :last_name, if: -> { player.display_name? }
332
271
 
333
- attribute \
334
- def composers
335
- ComposerSerializer.many(song.composers)
336
- end
272
+ has_one :album, serializer: AlbumSerializer, if: -> { player.album }
337
273
  end
338
274
  ```
339
275
 
340
- ## Other DSL 🛠
276
+ This is useful in cases where you don't want to `null` values to be in the response.
277
+
278
+ ## Advanced Usage 🧙‍♂️
341
279
 
342
280
  ### Using a different alias for the internal object
343
281
 
344
- You can use `object_as` to create an alias for the serialized object to access it from instance methods:
282
+ In most cases, the default alias for the `object` will be convenient enough.
283
+
284
+ However, if you would like to specify it manually, use `object_as`:
345
285
 
346
286
  ```ruby
347
287
  class DiscographySerializer < Oj::Serializer
348
288
  object_as :artist
349
289
 
350
290
  # Now we can use `artist` instead of `object` or `discography`.
291
+ attribute
351
292
  def latest_albums
352
293
  artist.albums.desc(:year)
353
294
  end
354
295
  end
355
296
  ```
356
297
 
357
- ### Rendering an attribute conditionally
298
+ ### Identifier attributes
358
299
 
359
- All the attributes and association methods can take an `if` option to render conditionally.
300
+ The `identifier` method allows you to only include an identifier if the record
301
+ or document has been persisted.
360
302
 
361
303
  ```ruby
362
304
  class AlbumSerializer < Oj::Serializer
363
- mongo_attributes :release_date, if: -> { album.released? }
305
+ identifier
306
+
307
+ # or if it's a different field
308
+ identifier :uuid
309
+ end
310
+ ```
311
+
312
+ Additionally, identifier fields are always rendered first, even when sorting
313
+ fields alphabetically.
314
+
315
+ ### Transforming attribute keys 🗝
316
+
317
+ When serialized data will be consumed from a client language that has different
318
+ naming conventions, it can be convenient to transform keys accordingly.
319
+
320
+ For example, when rendering an API to be consumed from the browser via JavaScript,
321
+ where properties are traditionally named using camel case.
322
+
323
+ Use `transform_keys` to handle that conversion.
324
+
325
+ ```ruby
326
+ class BaseSerializer < Oj::Serializer
327
+ transform_keys :camelize
328
+
329
+ # shortcut for
330
+ transform_keys -> (key) { key.to_s.camelize(:lower) }
331
+ end
332
+ ```
333
+
334
+ This has no performance impact, as keys will be transformed at load time.
335
+
336
+ ### Sorting attributes 📶
364
337
 
365
- has_many :songs, serializer: SongSerializer, if: -> { album.songs.any? }
338
+ By default attributes are rendered in the order they are defined.
366
339
 
367
- # You can achieve the same by manually defining a method:
368
- def include_songs?
369
- album.songs.any?
340
+ If you would like to sort attributes alphabetically, you can specify it at a
341
+ serializer level:
342
+
343
+ ```ruby
344
+ class BaseSerializer < Oj::Serializer
345
+ sort_attributes_by :name # or a Proc
346
+ end
347
+ ```
348
+
349
+ This has no performance impact, as attributes will be sorted at load time.
350
+
351
+ ### Path helpers 🛣
352
+
353
+ In case you need to access path helpers in your serializers, you can use the
354
+ following:
355
+
356
+ ```ruby
357
+ class BaseSerializer < Oj::Serializer
358
+ include Rails.application.routes.url_helpers
359
+
360
+ def default_url_options
361
+ Rails.application.routes.default_url_options
370
362
  end
371
363
  end
372
364
  ```
373
365
 
374
- ### Memoization & Local State
366
+ One slight variation that might make it easier to maintain in the long term is
367
+ to use a separate singleton service to provide the url helpers and options, and
368
+ make it available as `urls`.
375
369
 
376
- Serializers are designed to be stateless so that an instanced can be reused, but sometimes it's convenient to store intermediate calculations.
370
+ ### Memoization & local state
371
+
372
+ Serializers are designed to be stateless so that an instanced can be reused, but
373
+ sometimes it's convenient to store intermediate calculations.
377
374
 
378
375
  Use `memo` for memoization and storing temporary information.
379
376
 
@@ -381,7 +378,7 @@ Use `memo` for memoization and storing temporary information.
381
378
  class DownloadSerializer < Oj::Serializer
382
379
  attributes :filename, :size
383
380
 
384
- attribute \
381
+ attribute
385
382
  def progress
386
383
  "#{ last_event&.progress || 0 }%"
387
384
  end
@@ -396,9 +393,50 @@ private
396
393
  end
397
394
  ```
398
395
 
396
+ ### `hash_attributes` 🚀
397
+
398
+ Very convenient when serializing Hash-like structures, this strategy uses the `[]` operator.
399
+
400
+ ```ruby
401
+ class PersonSerializer < Oj::Serializer
402
+ hash_attributes 'first_name', :last_name
403
+ end
404
+
405
+ PersonSerializer.one('first_name' => 'Mary', :middle_name => 'Jane', :last_name => 'Watson')
406
+ # {first_name: "Mary", last_name: "Watson"}
407
+ ```
408
+
409
+ ### `mongo_attributes` 🚀
410
+
411
+ Reads data directly from `attributes` in a [Mongoid] document.
412
+
413
+ By skipping type casting, coercion, and defaults, it [achieves the best performance][raw_benchmarks].
414
+
415
+ Although there are some downsides, depending on how consistent your schema is,
416
+ and which kind of consumer the API has, it can be really powerful.
417
+
418
+ ```ruby
419
+ class AlbumSerializer < Oj::Serializer
420
+ mongo_attributes :id, :name
421
+ end
422
+ ```
423
+
399
424
  ### Caching 📦
400
425
 
401
- Use `cached` to leverage key-based caching, which calls `cache_key` in the object. You can also provide a lambda to `cached_with_key` to define a custom key:
426
+ Usually rendering is so fast that __turning caching on can be slower__.
427
+
428
+ However, in cases of deeply nested structures, unpredictable query patterns, or
429
+ methods that take a long time to run, caching can improve performance.
430
+
431
+ To enable caching, use `cached`, which calls `cache_key` in the object:
432
+
433
+ ```ruby
434
+ class CachedUserSerializer < UserSerializer
435
+ cached
436
+ end
437
+ ```
438
+
439
+ You can also provide a lambda to `cached_with_key` to define a custom key:
402
440
 
403
441
  ```ruby
404
442
  class CachedUserSerializer < UserSerializer
@@ -408,33 +446,164 @@ class CachedUserSerializer < UserSerializer
408
446
  end
409
447
  ```
410
448
 
411
- It will leverage `fetch_multi` when serializing a collection with `many` or `has_many`, to minimize the amount of round trips needed to read and write all items to cache. This works specially well if your cache store also supports `write_multi`.
449
+ It will leverage `fetch_multi` when serializing a collection with `many` or
450
+ `has_many`, to minimize the amount of round trips needed to read and write all
451
+ items to cache.
452
+
453
+ This works specially well if your cache store also supports `write_multi`.
454
+
455
+ ### Writing to JSON
456
+
457
+ In some corner cases it might be faster to serialize using a `Oj::StringWriter`,
458
+ which you can access by using `one_as_json` and `many_as_json`.
459
+
460
+ Alternatively, you can toggle this mode at a serializer level by using
461
+ `default_format :json`, or configure it globally from your base serializer:
462
+
463
+ ```ruby
464
+ class BaseSerializer < Oj::Serializer
465
+ default_format :json
466
+ end
467
+ ```
468
+
469
+ This will change the default shortcuts (`render`, `one`, `one_if`, and `many`),
470
+ so that the serializer writes directly to JSON instead of returning a Hash.
471
+
472
+ > **Note**
473
+ >
474
+ > This was the default behavior in `oj_serializers` v1, but was replaced with
475
+ `default_format :hash` in v2.
476
+
477
+ <details>
478
+ <summary>Example Output</summary>
479
+
480
+ ```json
481
+ {
482
+ "name": "Abraxas",
483
+ "genres": [
484
+ "Pyschodelic Rock",
485
+ "Blues Rock",
486
+ "Jazz Fusion",
487
+ "Latin Rock"
488
+ ],
489
+ "release": "September 23, 1970",
490
+ "songs": [
491
+ {
492
+ "track": 1,
493
+ "name": "Sing Winds, Crying Beasts",
494
+ "composers": [
495
+ "Michael Carabello"
496
+ ]
497
+ },
498
+ {
499
+ "track": 2,
500
+ "name": "Black Magic Woman / Gypsy Queen",
501
+ "composers": [
502
+ "Peter Green",
503
+ "Gábor Szabó"
504
+ ]
505
+ },
506
+ {
507
+ "track": 3,
508
+ "name": "Oye como va",
509
+ "composers": [
510
+ "Tito Puente"
511
+ ]
512
+ },
513
+ {
514
+ "track": 4,
515
+ "name": "Incident at Neshabur",
516
+ "composers": [
517
+ "Alberto Gianquinto",
518
+ "Carlos Santana"
519
+ ]
520
+ },
521
+ {
522
+ "track": 5,
523
+ "name": "Se acabó",
524
+ "composers": [
525
+ "José Areas"
526
+ ]
527
+ },
528
+ {
529
+ "track": 6,
530
+ "name": "Mother's Daughter",
531
+ "composers": [
532
+ "Gregg Rolie"
533
+ ]
534
+ },
535
+ {
536
+ "track": 7,
537
+ "name": "Samba pa ti",
538
+ "composers": [
539
+ "Santana"
540
+ ]
541
+ },
542
+ {
543
+ "track": 8,
544
+ "name": "Hope You're Feeling Better",
545
+ "composers": [
546
+ "Rolie"
547
+ ]
548
+ },
549
+ {
550
+ "track": 9,
551
+ "name": "El Nicoya",
552
+ "composers": [
553
+ "Areas"
554
+ ]
555
+ }
556
+ ]
557
+ }
558
+ ```
559
+ </details>
412
560
 
413
- Usually serialization happens so fast that __turning caching on can be slower__. Always benchmark to make sure it's worth it, and use caching only for time-consuming or deeply nested structures.
561
+ Even when using this mode, you can still use rendered values inside arrays,
562
+ hashes, and other serializers, thanks to [the `raw_json` extensions][raw_json].
414
563
 
415
564
  ## Design 📐
416
565
 
417
566
  Unlike `ActiveModel::Serializer`, which builds a Hash that then gets encoded to
418
- JSON, this implementation uses `Oj::StringWriter` to write JSON directly,
567
+ JSON, this implementation can use `Oj::StringWriter` to write JSON directly,
419
568
  greatly reducing the overhead of allocating and garbage collecting the hashes.
420
569
 
421
570
  It also allocates a single instance per serializer class, which makes it easy
422
571
  to use, while keeping memory usage under control.
423
572
 
573
+ The internal design is simple and extensible, and because the library is written
574
+ in Ruby, creating new serialization strategies requires very little code.
575
+ Please open a [Discussion] if you need help 😃
576
+
424
577
  ### Comparison with other libraries
425
578
 
426
579
  `ActiveModel::Serializer` instantiates one serializer object per item to be serialized.
427
580
 
428
- Other libraries such as [`jsonapi-serializer`][jsonapi] evaluate serializers in the context of
429
- a `class` instead of an `instance` of a class. Although it is efficient in terms
430
- of memory usage, the downside is that you can't use instance methods or local
431
- memoization, and any mixins must be applied to the class itself.
581
+ Other libraries such as [`blueprinter`][blueprinter] [`jsonapi-serializer`][jsonapi]
582
+ evaluate serializers in the context of a `class` instead of an `instance` of a class.
583
+ The downside is that you can't use instance methods or local memoization, and any
584
+ mixins must be applied to the class itself.
432
585
 
433
586
  [`panko-serializer`][panko] also uses `Oj::StringWriter`, but it has the big downside of having to own the entire render tree. Putting a serializer inside a Hash or an Active Model Serializer and serializing that to JSON doesn't work, making a gradual migration harder to achieve. Also, it's optimized for Active Record but I needed good Mongoid support.
434
587
 
435
588
  `Oj::Serializer` combines some of these ideas, by using instances, but reusing them to avoid object allocations. Serializing 10,000 items instantiates a single serializer. Unlike `panko-serializer`, it doesn't suffer from [double encoding problems](https://panko.dev/docs/response-bag) so it's easier to use.
436
589
 
437
- As a result, migrating from `active_model_serializers` is relatively straightforward because instance methods, inheritance, and mixins work as usual.
590
+ Follow [this discussion][raw_json] to find out more about [the `raw_json` extensions][raw_json] that made this high level of interoperability possible.
591
+
592
+ As a result, migrating from `active_model_serializers` is relatively
593
+ straightforward because instance methods, inheritance, and mixins work as usual.
594
+
595
+ ### Benchmarks 📊
596
+
597
+ This library includes some [benchmarks] to compare performance with similar libraries.
598
+
599
+ See [this pull request](https://github.com/ElMassimo/oj_serializers/pull/9) for a quick comparison,
600
+ or check the CI to see the latest results.
601
+
602
+ ### Migrating from other libraries
603
+
604
+ Please refer to the [migration guide] for a full discussion of the compatibility
605
+ modes available to make it easier to migrate from `active_model_serializers` and
606
+ similar libraries.
438
607
 
439
608
  ## Formatting 📏
440
609