package ppx_deriving_yaml

  1. Overview
  2. Docs

Deriving Yaml

This ppx is based on ppx_yojson and ppx_deriving_yojson because of the many similarities between JSON and yaml.

So similar that OCaml's Yaml library shares a common type with the Ezjsonm library. See ppx_deriving_ezjsonm for more details.

Basic Usage

For converting OCaml values to Yaml values ppx_deriving_yaml will do the conventional dropping of the type name if it is t. Otherwise the type name is the prefix to the to_yaml function.

to_yaml produces a Yaml.value.

of_yaml produces OCaml types wrapped in a Stdlib.result.

# #require "ppx_deriving_yaml";;

Here is a small example.

type person = { name : string; age : int } [@@deriving yaml]
type users = person list [@@deriving yaml]

This will produce four functions, a _to_yaml and _of_yaml for both a person and the users. For example:

# person_to_yaml;;
- : person ->
    [> `O of (string * [> `Float of float | `String of string ]) list ]
= <fun>
# users_of_yaml;;
- : [> `A of
         [> `O of (string * [> `Float of float | `String of string ]) list ]
         list ] ->
    (person list, [> `Msg of string ]) result
= <fun>

If your type constructors have arguments, then the functions will be higher-order and you will need to supply a function to convert values for each constructor argument. For example:

type 'a note = { txt : 'a } [@@deriving yaml]

produces the following function.

# note_to_yaml;;
- : ('a -> Yaml.value) -> 'a note -> [> `O of (string * Yaml.value) list ] =
<fun>

Finally, if you only need the encoder (to_yaml) or the decoder (of_yaml) then there are single versions of the deriver for those.

# type x = { age : int }[@@deriving to_yaml];;
type x = { age : int; }
val x_to_yaml : x -> [> `O of (string * [> `Float of float ]) list ] = <fun>

Attributes

Key and Name

Record field names cannot begin with a capital letter and variant constructors must start with one. This limits what the generated Yaml can look like. To override the Yaml names you can use the [@key <string>] and [@name <string>] attributes for records and variants respectively.

# type t = {
    camel_name : string [@key "camel-name"]
  }[@@deriving to_yaml];;
type t = { camel_name : string; }
val to_yaml : t -> [> `O of (string * [> `String of string ]) list ] = <fun>

# Yaml.to_string_exn (to_yaml { camel_name = "Alice" });;
- : string = "camel-name: Alice\n"

Default Values

You can also specify default values for fields.

type t = {
  name : string;
  age : int [@default 42]
}[@@deriving yaml]

These will be used in the absence of any fields when decoding Yaml values into OCaml ones.

# Yaml.of_string_exn "name: Alice" |> of_yaml;;
- : (t, [> `Msg of string ]) result = Ok {name = "Alice"; age = 42}

Custom encoding and decoding

Sometimes you might want to specify your own encoding and decoding logic on field by field basis. To do so, you can use the of_yaml and to_yaml attributes.

type t = {
  name : string [@to_yaml fun i -> `String ("custom-" ^ i)]
}[@@deriving yaml]

The to_yaml function will use the custom encoder now instead.

# Yaml.to_string_exn (to_yaml { name = "alice" });;
- : string = "name: custom-alice\n"

Partially Decoding

There is a ~skip_unknown flag for telling the deriver to simply ignore any fields which are missing. This is particularly useful when you only wish to partially decode a yaml value.

Consider the following yaml:

let yaml = "name: Bob\nage: 42\nmisc: We don't need this!"

If we try to do the normal decoding of this but only partially extract the fields, it will throw an error.

# type t = {
    name : string;
    age : int;
  }[@@deriving of_yaml];;
type t = { name : string; age : int; }
val of_yaml :
  [> `O of (string * [> `Float of float | `String of string ]) list ] ->
  (t, [> `Msg of string ]) result = <fun>

# Yaml.of_string_exn yaml |> of_yaml;;
- : (t, [> `Msg of string ]) result =
Error (`Msg "Failed to find the case for: misc")

Instead we tell the deriver to ignore unknown fields.

type t = {
  name : string;
  age : int;
}[@@deriving of_yaml ~skip_unknown]
# Yaml.of_string_exn yaml |> of_yaml;;
- : (t, [> `Msg of string ]) result = Ok {name = "Bob"; age = 42}

Implementation Details

One important thing is that 'a option values within records will return None if the Yaml you are trying to convert does not exist.

OCaml Type

Yaml Type

int

`Float

float

`Float

string

`String

bool

`Bool

None

`Null

list

`A []

array

`A []

record e.g { name : string }

`O [("name", `String s)]

A of int or [`A of int]

`O [("A", `A [`Float f])]

OCaml

Innovation. Community. Security.