Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
064402c
commit 4c8e3ca
Showing
1 changed file
with
37 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,20 +5,23 @@ | |
Erlang-Version: | ||
Post-History: | ||
**** | ||
# EEP 68: JSON library | ||
EEP 68: JSON library | ||
---- | ||
|
||
## Abstract | ||
Abstract | ||
======== | ||
|
||
This EEP proposes introducing a module `json` to the Erlang standard | ||
library with support for encoding and decoding [JSON][1] documents | ||
from and to Erlang data structures. The main reason is to cover | ||
a gap in the Erlang standard library with regards to such a vastly | ||
popular and widespread data format. | ||
|
||
## Rationale | ||
Rationale | ||
========= | ||
|
||
JSON is commonly used in many different use-cases: | ||
|
||
* by web services as a lightweight and human-readable data interchange format; | ||
* as a configuration language in static files; | ||
* as data interchange format by developer tooling; | ||
|
@@ -44,6 +47,7 @@ as an extensible and highly-customisable API with common underlying | |
implementation. | ||
|
||
This EEP proposes a JSON library which: | ||
|
||
* should be easy to adopt in large codebases using one of the popular, | ||
existing, open-source JSON libraries; | ||
* will allow the existing open-source libraries with custom features | ||
|
@@ -53,6 +57,7 @@ This EEP proposes a JSON library which: | |
leading open-source JSON libraries. | ||
|
||
The proposed JSON library will provide: | ||
|
||
* JSON encoding, allowing for single-pass encoding of custom data types –- | ||
in particular, for Elixir, integrating with a protocol through a thin layer | ||
(implemented outside of OTP); | ||
|
@@ -65,9 +70,11 @@ The proposed JSON library will provide: | |
the decoder should pass the entire [JSONTestSuite][JSONTestSuite]; | ||
* simple API for common use-cases with canonical data type mapping. | ||
|
||
## Design choices | ||
Design choices | ||
============== | ||
|
||
### Data mapping | ||
Data mapping | ||
------------ | ||
|
||
We propose, in the "canonical" API to map JSON data structues to | ||
Erlang and back in the following way: | ||
|
@@ -97,7 +104,8 @@ even with custom decoders -- since JSON has such a limited data-type | |
options, compared to Erlang, some information will be commonly be lost, | ||
for example, coercing all keys in maps to binaries. | ||
|
||
### Streaming vs value-based parser | ||
Streaming vs value-based parser | ||
------------------------------- | ||
|
||
When it comes to data-structure parsers it's common to encounter two | ||
types: ones that given the data produce a complete parsed value, | ||
|
@@ -128,6 +136,7 @@ First, a simple, value-based API: | |
|
||
Error handling is achieved through exceptions. The following errors | ||
are possible: | ||
|
||
```erlang | ||
Check failure on line 140 in eeps/eep-0068.md GitHub Actions / markdownlintCode block style
|
||
-type error() :: | ||
unexpected_end | | ||
|
@@ -169,6 +178,7 @@ API that the decoder will use to produce values from the data it parses. | |
|
||
This allows the user to fully customize the decoded format, including | ||
features seen in open-source JSON libraries: | ||
|
||
* decoding string keys as atoms; | ||
* decoding objects as lists of pairs; | ||
* decoding floats as custom structures with decimal precision; | ||
|
@@ -201,7 +211,8 @@ All the callbacks are optional and have a default value corresponding to the | |
* for `string`: `fun (Value) -> Value end` | ||
* for `null`: the atom `null` | ||
### Incomplete data parsing | ||
Incomplete data parsing | ||
----------------------- | ||
We propose a future enhancement to the full `decode/3` API, where | ||
it can return an `{incomplete, continuation()}` value that can be used to | ||
|
@@ -214,7 +225,8 @@ from a TCP socket). | |
{incomplete, continuation()}. | ||
``` | ||
### Encoding API | ||
Encoding API | ||
------------ | ||
For encoding this EEP again proposes two separate sets of APIs. | ||
A simple API using "canonical" data types: | ||
|
@@ -268,7 +280,8 @@ that will always produce purely ASCII messages encoding all higher | |
unicode values with the `\u` escape sequences. | ||
|
||
|
||
Check failure on line 282 in eeps/eep-0068.md GitHub Actions / markdownlintMultiple consecutive blank lines
|
||
### Formatting and pretty-printing | ||
Formatting and pretty-printing | ||
------------------------------ | ||
|
||
This EEP further proposes an additional API for formatting (and pretty-printing) | ||
JSON messages. This API consists of transforming a textual JSON message into | ||
|
@@ -290,22 +303,28 @@ services, therefore the overhead of a two-pass formatting is deemed acceptable. | |
-spec format(iodata(), format_option()) -> iodata(). | ||
``` | ||
## Reference Implementation | ||
Reference Implementation | ||
======================== | ||
[PR-8111][PR] Implements the `encode/1`, `encode/2`, `decode/1`, and `decode/3` | ||
functions as proposed in this EEP. | ||
The formatting API and the support for incomplete message decoding is left | ||
as a follow-up task. | ||
## Appendix | ||
Appendix | ||
======== | ||
### Example of a decoding trace | ||
Example of a decoding trace | ||
--------------------------- | ||
Given the following data: | ||
```json | ||
Check failure on line 322 in eeps/eep-0068.md GitHub Actions / markdownlintCode block style
|
||
{"a": [[], {}, true, false, null, {"foo": "baz"}], "b": [1, 2.0, "three"]} | ||
``` | ||
the decoding APIs will be called with the following arguments: | ||
```erlang | ||
Check failure on line 328 in eeps/eep-0068.md GitHub Actions / markdownlintCode block style
|
||
object_start(Acc0) => Acc1 | ||
string(<<"a">>) => Str1 | ||
|
@@ -341,11 +360,13 @@ object_finish(Acc18, Acc0) => {Obj3, Acc19} | |
{Obj3, Acc19, <<"">>} | ||
``` | ||
### Example of a custom encoder | ||
Example of a custom encoder | ||
--------------------------- | ||
An example of a custom encoder that would support using a heuristic | ||
to differentiate pairs of object-like key-value lists from plain | ||
lists of values could look as follows: | ||
```erlang | ||
custom_encode(Value) -> json:encode(Value, fun encoder/2). | ||
|
@@ -355,6 +376,7 @@ encoder(Other, Encode) -> json:encode_value(Other, Encode). | |
Another encoder that supports using Elixir `nil` as Null and protocols for | ||
further customisation could look as follows: | ||
```erlang | ||
encoder(nil, _Encode) -> <<"null">>; | ||
encoder(null, _Encode) -> <<"\"null\"">>; | ||
|
@@ -381,7 +403,8 @@ encoder(Other, Encode) -> json:encode_value(Other, Encode). | |
[PR]: https://github.com/erlang/otp/pull/8111 | ||
## Copyright | ||
Copyright | ||
========= | ||
This document is placed in the public domain or under the CC0-1.0-Universal | ||
license, whichever is more permissive. |