Skip to content

Commit ab37f33

Browse files
committed
Document serialization
1 parent 4b366e4 commit ab37f33

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,3 +264,78 @@ julia> pyonprint(stdout, [1, 2, nothing])
264264
julia> sprint(pyonprint, missing)
265265
"None"
266266
```
267+
268+
#### Serialization
269+
270+
For cases where the JSON cosmetics are unimportant, but how objects are converted into their
271+
JSON equivalents (arrays, objects, numbers, etc.) need to be changed, the appropriate
272+
abstraction is `Serialization`. A `Serialization` instance is used as the second argument in
273+
`show_json`. Thus, specializing `show_json` for custom `Serialization` instances enables
274+
either creating more restrictive or different ways to convert objects into JSON.
275+
276+
The default serialization is called `JSON.Serializations.StandardSerialization`, which is a
277+
subtype of `CommonSerialization`. Methods of `show_json` are not added to
278+
`StandardSerialization`, but rather to `CommonSerialization`, by both `JSON` and by
279+
other packages for their own types. The `lower` functionality is also specific to
280+
`CommonSerialization`. Therefore, to create a serialization instance that inherits from and
281+
may extend or override parts of the standard serialization, it suffices to define a new
282+
serialization subtyping `CommonSerialization`. In the example below, the new serialization
283+
is the same as `StandardSerialization` except that numbers are serialized with an additional
284+
type tag.
285+
286+
```julia
287+
import JSON.Serializations: CommonSerialization, StandardSerialization
288+
import JSON.Writer: StructuralContext, show_json
289+
struct TaggedNumberSerialization <: CommonSerialization end
290+
291+
tag(f::Real) = Dict(:type => string(typeof(f)), :value => f)
292+
show_json(io::StructuralContext,
293+
::TaggedNumberSerialization,
294+
f::Union{Integer, AbstractFloat}) =
295+
show_json(io, StandardSerialization(), tag(f))
296+
```
297+
298+
Note that the recursive call constructs a `StandardSerialization()`, as otherwise this would
299+
result in a stack overflow, and serializes a `Dict` using that. In this toy example, this is
300+
fine (with only the overhead of constructing a `Dict`), but this is not always possible.
301+
(For instance, if the constructed `Dict` could have other numbers within its values that
302+
need to be tagged.)
303+
304+
To deal with these more complex cases, or simply to eliminate the overhead of constructing
305+
the intermediate `Dict`, the `show_json` method can be implemented more carefully by
306+
explicitly calling the context’s `begin_object`, `show_pair`, and `end_object` methods, as
307+
documented above, and use the `StandardSerialization()` only for the `show_pair` call for
308+
`f`.
309+
310+
```julia
311+
# More careful implementation
312+
# No difference in this case, but could be needed if recursive data structures are to be
313+
# serialized in more complex cases.
314+
import JSON.Writer: begin_object, show_pair, end_object
315+
function show_json(io::StructuralContext,
316+
s::TaggedNumberSerialization,
317+
f::Union{Integer, AbstractFloat})
318+
begin_object(io)
319+
show_pair(io, s, :tag => string(typeof(f)))
320+
show_pair(io, StandardSerialization(), :value => f)
321+
end_object(io)
322+
end
323+
```
324+
325+
To use the custom serialization, `sprint` can be used (and this can be encapsulated by a
326+
convenient user-defined inteface):
327+
328+
```julia
329+
julia> JSON.parse(sprint(show_json, TaggedNumberSerialization(), Any[1, 2.0, "hi"]))
330+
3-element Array{Any,1}:
331+
Dict{String,Any}("value" => 1,"type" => "Int64")
332+
Dict{String,Any}("value" => 2.0,"type" => "Float64")
333+
"hi"
334+
```
335+
336+
If it is not desired to inherit all the functionality of `StandardSerialization`, users may
337+
also choose to start from scratch by directly subtyping `JSON.Serializations.Serialization`.
338+
This is useful if the user wishes to enforce a strict JSON which throws errors when
339+
attempting to serialize objects that aren’t explicitly supported. Note that this means you
340+
will need to define a method to support serializing any kind of object, including the
341+
standard JSON objects like booleans, integers, strings, etc.!

0 commit comments

Comments
 (0)