***************
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.
