package ppx_deriving_yamlx

  1. Overview
  2. Docs
Yamlx ppx

Install

dune-project
 Dependency

Authors

Maintainers

Sources

ppx_deriving_yaml-0.5.0.tbz
sha256=6e99f280519dcc962697292f77678917c69a5ba8ee3c93ea05d329115dcaca5a
sha512=326a597fc8d53fc6ab8dcbf3f534239ce0fc0823380f5c11da75b5c3b55d47f7206db5648113070bef2bd668da37f60f70b7bb42ea6ec99d8209fddbc0edd2a0

doc/index.html

Deriving Yaml

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

Basic Usage

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

to_yamlx produces a YAMLx.value.

of_yamlx produces OCaml types wrapped in a Stdlib.result.

Here is a small example.

# type person = { name : string; age : int } [@@deriving yamlx]
type person = { name : string; age : int; }
val person_to_yamlx : person -> YAMLx.value = <fun>
val person_of_yamlx : YAMLx.value -> (person, [> `Msg of string ]) result =
  <fun>
# type users = person list [@@deriving yamlx]
type users = person list
val users_to_yamlx : person list -> YAMLx.value = <fun>
val users_of_yamlx : YAMLx.value -> (person list, [> `Msg of string ]) result =
  <fun>

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

# person_to_yamlx;;
- : person -> YAMLx.value = <fun>
# users_of_yamlx;;
- : YAMLx.value -> (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 yamlx]

produces the following function.

# note_to_yamlx;;
- : ('a -> YAMLx.value) -> 'a note -> YAMLx.value = <fun>

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

# type x = { age : int }[@@deriving to_yamlx];;
type x = { age : int; }
val x_to_yamlx : x -> YAMLx.value = <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_yamlx];;
type t = { camel_name : string; }
val to_yamlx : t -> YAMLx.value = <fun>

# YAMLx.Value.to_yaml (to_yamlx { 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 yamlx]

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

# YAMLx.Value.of_yaml_exn "name: Alice" |> of_yamlx;;
- : (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_yamlx and to_yamlx attributes.

type t = {
  name : string [@to_yamlx fun i -> YAMLx.String (YAMLx.zero_loc, ("custom-" ^ i))]
}[@@deriving yamlx]

The to_yamlx function will use the custom encoder now instead.

# YAMLx.Value.to_yaml (to_yamlx { 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_yamlx];;
type t = { name : string; age : int; }
val of_yamlx : YAMLx.value -> (t, [> `Msg of string ]) result = <fun>

# YAMLx.Value.of_yaml_exn yaml |> of_yamlx;;
- : (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_yamlx ~skip_unknown]
# YAMLx.Value.of_yaml_exn yaml |> of_yamlx;;
- : (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

Yamlx Type

int

YAMLx.Int

float

YAMLx.Float

string

YAMLx.String

bool

YAMLx.Bool

None

YAMLx.Null

list

YAMLx.Seq

array

YAMLx.Seq

record e.g { name : string }

YAMLx.Map [(_, YAMLx.String (_ ,"name"), YAMLx.String (_, s))]

A of int or [`A of int]

YAMLx.Map [(_, YAMLx.String (_, "A"), YAMLx.Seq (_, [ YAMLx.Float (_, f)]))]