These guidelines provides recommendations for defining JSON data at Pon. JSON here refers to {RFC-7159}[RFC 7159] (which updates {RFC-4627}[RFC 4627]), the "application/json" media type and custom JSON media types defined for APIs. The guidelines clarifies some specific cases to allow Pon JSON data to have an idiomatic form across teams and services.
The first some of the following guidelines are about property names, the later ones about values.
Property names are restricted to ASCII strings. The first character must be a letter, or an underscore, and subsequent characters can be a letter, an underscore, or a number.
(It is recommended to use _
at the start of property names only for keywords like _links
.)
Rationale: No established industry standard exists, but many popular Internet companies prefer snake_case: e.g. GitHub, Stack Exchange, Twitter. Others, like Google and Amazon, use both - but not only camelCase. It’s essential to establish a consistent look and feel such that JSON looks as if it came from the same hand.
Enum values (using enum
or {x-extensible-enum}) need to consistently use the upper-snake case format, e.g. VALUE
or YET_ANOTHER_VALUE
. This approach allows to clearly distinguish values from properties or other elements.
A "map" here is a mapping from string keys to some other type. In JSON this is represented as an object, the key-value pairs being represented by property names and property values. In Open API schema (as well as in JSON schema) they should be represented using additionalProperties with a schema defining the value type. Such an object should normally have no other defined properties.
The map keys don’t count as property names in the sense of rule 118, and can follow whatever format is natural for their domain. Please document this in the description of the map object’s schema.
Here is an example for such a map definition (the translations
property):
components:
schemas:
Message:
description:
A message together with translations in several languages.
type: object
properties:
message_key:
type: string
description: The message key.
translations:
description:
The translations of this message into several languages.
The keys are [IETF BCP-47 language tags](https://tools.ietf.org/html/bcp47).
type: object
additionalProperties:
type: string
description:
the translation of this message into the language identified by the key.
An actual JSON object described by this might then look like this:
{ "message_key": "color",
"translations": {
"de": "Farbe",
"en-US": "color",
"en-GB": "colour",
"eo": "koloro",
"nl": "kleur"
}
}
To indicate they contain multiple values prefer to pluralize array names. This implies that object names should in turn be singular.
Schema based JSON properties that are by design booleans must not be presented as nulls. A boolean is essentially a closed enumeration of two values, true and false. If the content has a meaningful null value, strongly prefer to replace the boolean with enumeration of named values or statuses - for example accepted_terms_and_conditions with true or false can be replaced with terms_and_conditions with values yes, no and unknown.
Open API 3.x allows to mark properties as required
and as nullable
to
specify whether properties may be absent ({}
) or null
({"example":null}
).
If a property is defined to be not required
and nullable
(see
2nd row in Table below), this rule demands
that both cases must be handled in the exact same manner by specification.
The following table shows all combinations and whether the examples are valid:
{CODE-START}required{CODE-END} | {CODE-START}nullable{CODE-END} | {CODE-START}{}{CODE-END} | {CODE-START}{"example":null}{CODE-END} |
---|---|---|---|
|
|
{NO} |
{YES} |
|
|
{YES} |
{YES} |
|
|
{NO} |
{NO} |
|
|
{YES} |
{NO} |
While API designers and implementers may be tempted to assign different semantics to both cases, we explicitly decide against that option, because we think that any gain in expressiveness is far outweighed by the risk of clients not understanding and implementing the subtle differences incorrectly.
As an example, an API that provides the ability for different users to
coordinate on a time schedule, e.g. a meeting, may have a resource for options
in which every user has to make a choice
. The difference between undecided
and decided against any of the options could be modeled as absent and
null
respectively. It would be safer to express the null
case with a
dedicated Null object, e.g.
{}
compared to {"id":"42"}
.
Moreover, many major libraries have somewhere between little to no support for
a null
/absent pattern (see
Gson,
Moshi,
Jackson,
JSON-B). Especially
strongly-typed languages suffer from this since a new composite type is required
to express the third state. Nullable Option
/Optional
/Maybe
types could be
used but having nullable references of these types completely contradicts their
purpose.
The only exception to this rule is JSON Merge Patch {RFC-7396}[RFC 7396]) which
uses null
to explicitly indicate property deletion while absent properties are
ignored, i.e. not modified.
Empty array values can unambiguously be represented as the empty list, []
.
Strings are a reasonable target for values that are by design enumerations.
Dates and date-time properties should end with _at
to distinguish them from
boolean properties which otherwise would have very similar or even identical
names:
-
{created_at} rather than {created},
-
{modified_at} rather than {modified},
-
occurred_at
rather thanoccurred
, and -
returned_at
rather thanreturned
.
Note: {created} and {modified} were mentioned in an earlier version of the guideline and are therefore still accepted for APIs that predate this rule.
Use the date and time formats defined by {RFC-3339}#section-5.6[RFC 3339]:
-
for "date" use strings matching
date-fullyear "-" date-month "-" date-mday
, for example:2015-05-28
-
for "date-time" use strings matching
full-date "T" full-time
, for example2015-05-28T14:07:17Z
Note that the
Open
API format "date-time" corresponds to "date-time" in the RFC) and 2015-05-28
for a date (note that the Open API format "date" corresponds to "full-date" in
the RFC). Both are specific profiles, a subset of the international standard
{ISO-8601}[ISO 8601].
A zone offset may be used (both, in request and responses) — this is simply
defined by the standards. However, we encourage restricting dates to UTC and
without offsets. For example 2015-05-28T14:07:17Z
rather than
2015-05-28T14:07:17+00:00
. From experience we have learned that zone offsets
are not easy to understand and often not correctly handled. Note also that
zone offsets are different from local times that might be including daylight
saving time. Localization of dates should be done by the services that provide
user interfaces, if required.
When it comes to storage, all dates should be consistently stored in UTC without a zone offset. Localization should be done locally by the services that provide user interfaces, if required.
Sometimes it can seem data is naturally represented using numerical timestamps, but this can introduce interpretation issues with precision, e.g. whether to represent a timestamp as 1460062925, 1460062925000 or 1460062925.000. Date strings, though more verbose and requiring more effort to parse, avoid this ambiguity.