Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
unreleased
----------

* Support alternative representations of variants
(#155)
Anna Danilkin

3.10.0
------

Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ The following table summarizes the correspondence between OCaml types and JSON v
| `Yojson.Safe.t` | any | Identity transformation |
| `unit` | Null | |

Variants (regular and polymorphic) are represented using arrays; the first element is a string with the name of the constructor, the rest are the arguments. Note that the implicit tuple in a polymorphic variant is flattened. For example:
By default, variants (regular and polymorphic) are represented using arrays; the first element is a string with the name of the constructor, the rest are the arguments. Note that the implicit tuple in a polymorphic variant is flattened. For example:

``` ocaml
# type pvs = [ `A | `B of int | `C of int * string ] list [@@deriving yojson];;
Expand All @@ -97,7 +97,20 @@ Record variants are represented in the same way as if the nested structure was d
["X",{"v":0}]
```

Record variants are currently not supported for extensible variant types.
Alternative representations of variants can be chosen using the option `variants` (e.g. ``[@@deriving yojson { variants = `Adjacent ("tag", "contents") }]``). The following ones are supported:
1. ``variants = `Array`` (default): variants are encoded as an array, where the first element is the constructor name and the subsequent elements are the constructor arguments.
2. ``variants = `External``: variants are encoded as just the tag for empty constructors and as an object with a single field named after the constructor name and with the constructor arguments as its value otherwise.
3. ``variants = `Internal "type"``: variants are encoded as an object containing a field named `"type"` with the constructor name as its value, as well as the fields of the inline record, if present; tuple constructors are not supported.
4. ``variants = `Adjacent ("tag", "contents")``: variants are encoded as an object containing a field named `"tag"` with the constructor name as its value, and a field named `"contents"` with the constructor arguments as its value, if present.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updated documentation. If I understand correctly, you took care to implement those options for interoperability with the representation of variants in other libraries (serde, aeson, etc.). Which of those representations are interoperable with which libraries? Maybe it would be useful to mention it in the documentation here.


Here is a table of examples of how the encodings look like:

| `type _ =` | Value | ``variants = `Array`` | ``variants = `External`` | ``variants = `Internal "type"`` | ``variants = `Adjacent ("tag", "contents")`` |
| ------------------------- | ---------------- | ---------------------- | ------------------------ | ------------------------------- | -------------------------------------------- |
| `\| RA` | `RA` | `["RA"]` | `"RA"` | `{"type": "RA"}` | `{"tag": "RA"}` |
| `\| RB of int` | `RB 42` | `["RB", 42]` | `{"RB": 42}` | (not supported) | `{"tag": "RB", "contents": 42}` |
| `\| RC of int * string` | `RC (42, "foo")` | `["RC", 42, "foo"]` | `{"RC": [42, "foo"]}` | (not supported) | `{"tag": "RC", "contents": [42, "foo"]}` |
| `\| RD of { z : string }` | `RD {z = "foo"}` | `["RD", {"z": "foo"}]` | `{"RD": {"z": "foo"}}` | `{"type": "RD", "z": "foo"}` | `{"tag": "RD", "contents": {"z": "foo"}}` |

By default, objects are deserialized strictly; that is, all keys in the object have to correspond to fields of the record. Passing `strict = false` as an option to the deriver (i.e. `[@@deriving yojson { strict = false }]`) changes the behavior to ignore any unknown fields.

Expand Down
Loading