File: json_serializer.rst

package info (click to toggle)
python-stone 3.3.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,036 kB
  • sloc: python: 22,311; objc: 498; sh: 23; makefile: 11
file content (207 lines) | stat: -rw-r--r-- 4,986 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
***************
JSON Serializer
***************

Code generators include a JSON serializer which will convert a target
language's representation of Stone data types into JSON. This document explores
how Stone data types, regardless of language, are mapped to JSON.

Primitive Types
===============

========================== ====================================================
Stone Primitive            JSON Representation
========================== ====================================================
Boolean                    Boolean
Bytes                      String: Base64-encoded
Float{32,64}               Number
Int{32,64}, UInt{32,64}    Number
List                       Array
String                     String
Timestamp                  String: Encoded using strftime() based on the
                           Timestamp's format argument.
Void                       Null
========================== ====================================================

Struct
======

A struct is represented as a JSON object. Each specified field has a key in the
object. For example::

    struct Coordinate
        x Int64
        y Int64


converts to::

    {
     "x": 1,
     "y": 2
    }

If an optional (has a default or is nullable) field is not specified, the key
should be omitted. For example, given the following spec::

    struct SurveyAnswer
        age Int64
        name String = "John Doe"
        address String?

If ``name`` and ``address`` are unset and ``age`` is 28, then the struct
serializes to::

    {
     "age": 28
    }

Setting ``name`` or ``address`` to ``null`` is not a valid serialization;
deserializers will raise an error.

An explicit ``null`` is allowed for fields with nullable types. While it's
less compact, this makes serialization easier in some languages. The previous
example could therefore be represented as::

    {
     "age": 28,
     "address": null
    }

Enumerated Subtypes
-------------------

A struct that enumerates subtypes serializes similarly to a regular struct,
but includes a ``.tag`` key to distinguish the type. Here's an example to
demonstrate::

    struct A
        union*
            b B
            c C
        w Int64

    struct B extends A
        x Int64

    struct C extends A
        y Int64

Serializing ``A`` when it contains a struct ``B`` (with values of ``1`` for
each field) appears as::

    {
     ".tag": "b",
     "w": 1,
     "x": 1
    }

If the recipient receives a tag it cannot match to a type, it should fallback
to the parent type if it's specified as a catch-all.

For example::

    {
     ".tag": "d",
     "w": 1,
     "z": 1
    }

Because ``d`` is unknown, the recipient checks that struct ``A`` is a
catch-all. Since it is, it deserializes the message to an ``A`` object.

Union
=====

Similar to an enumerated subtype struct, recipients should check the ``.tag``
key to determine the union variant.

Let's use the following example to illustrate how a union is serialized based
on the selected variant::

    union U
        singularity
        number Int64
        coord Coordinate?
        infinity Infinity

    struct Coordinate
        x Int64
        y Int64

    union Infinity
        positive
        negative

The serialization of ``U`` with tag ``singularity`` is::

    {
     ".tag": "singularity"
    }

For a union member of primitive type (``number`` in the example), the
serialization is as follows::

    {
     ".tag": "number",
     "number": 42
    }

Note that ``number`` is used as the value for ``.tag`` and as a key to hold
the value. This same pattern is used for union members with types that are
other unions or structs with enumerated subtypes.

Union members that are ordinary structs (``coord`` in the example) serialize
as the struct with the addition of a ``.tag`` key. For example, the
serialization of ``Coordinate`` is::

    {
     "x": 1,
     "y": 2
    }

The serialization of ``U`` with tag ``coord`` is::

    {
     ".tag": "coord",
     "x": 1,
     "y": 2
    }

The serialization of ``U`` with tag ``infinity`` is nested::

    {
     ".tag": "infinity",
     "infinity": {
      ".tag": "positive"
     }
    }

The same rule applies for members that are enumerated subtypes.

Nullable
--------

Note that ``coord`` references a nullable type. If it's unset, then the
serialization only includes the tag::

    {
     ".tag": "coord"
    }

You may notice that if ``Coordinate`` was defined to have no fields, it is
impossible to differentiate between an unset value and a value of coordinate.
In these cases, we prescribe that the deserializer should return a null
or unset value.

Compact Form
------------

Deserializers should support an additional representation of void union
members: the tag itself as a string. For example, tag ``singularity`` could
be serialized as simply::

    "singularity"

This is convenient for humans manually entering the argument, allowing them to
avoid typing an extra layer of JSON object nesting.