Decoders_sexplib.Decode
Turn S-expressions into Ocaml values via Sexplib.
Following the convention of Sexplib0.Sexp_conv.hashtbl_of_sexp
, we consider an S-expression to be an "object" if it is a list of two-element lists. For example:
((field1 value1) (field2 (value2 value3)))
Following `dune` conventions, we also allow as "objects" S-expressions like:
((field1 value1) (field2 value2 value3))
These two S-expressions will be treated in the same way by the object combinators below (e.g. field
).
Like YAML, fields of an object are not necessarily atoms. To handle these, look for the primed combinators (e.g. keys'
).
include Decoders.Decode.S with type value = Sexplib0.Sexp.t
type error = value Decoders.Error.t
val pp_error : Stdlib.Format.formatter -> error -> unit
val string_of_error : error -> string
val of_string : string -> ( value, error ) Decoders.Util.My_result.t
val of_file : string -> ( value, error ) Decoders.Util.My_result.t
type 'a decoder = ( value, 'a ) Decoders.Decoder.t
The type of decoders.
Use the functions below to construct decoders for your data types.
To run a decoder, pass it to decode_value
.
val string : string decoder
Decode a string
.
val int : int decoder
Decode an int
.
val float : float decoder
Decode a float
.
val bool : bool decoder
Decode a bool
.
val null : unit decoder
Decode a null
.
Decode a collection into an OCaml list, skipping elements for which the decoder returns None.
Decode a collection with an accumulator.
If we consider that an 'a decoder
is basically a type alias for json -> ('a, error) result
, the signature of this function is comparable to that of List.fold_left
:
val List.fold_left : ('a -> 'b -> 'a) -> 'a -> 'b list -> 'a
val list_fold_left : ('a -> json -> ('a, error) result) -> 'a -> json -> ('a, error) result
val list_fold_left : ('a -> 'a decoder) -> 'a -> 'a decoder
fst |> uncons rest
decodes the first element of a list using fst
, then decodes the remainder of the list using rest
.
For example, to decode this s-expression:
(library
(name decoders))
we can use this decoder:
string |> uncons (function
| "library" -> field "name" string
| _ -> fail "Expected a library stanza")
As another example, say you have a JSON array that starts with a string, then a bool, then a list of integers:
["hello", true, 1, 2, 3, 4]
We could decode it like this:
let (>>=::) fst rest = uncons rest fst
let decoder : (string * bool * int list) decoder =
string >>=:: fun the_string ->
bool >>=:: fun the_bool ->
list int >>= fun the_ints ->
succeed (the_string, the_bool, the_ints)
(If you squint, the uncons operator >>=::
kind of looks like the cons operator ::
.)
Decode an object, where a particular field may or may not be present.
For example, (field_opt "hello" int)
:
{"hello": 123}
, will succeed with Some 123
{"hello": null}
, will fail{"world": 123}
, will succeed with None
["a", "list", "of", "strings"]
, will failSimilar to field_opt
but with a default value.
Decode an object, requiring exactly one field.
maybe d
is a decoder that always succeeds. If d
succeeds with x
, then maybe d
succeeds with Some x
, otherwise if d
fails, then maybe d
succeeds with None
.
For example, maybe (field "hello" int)
:
{"hello": 123}
, will succeed with Some 123
{"hello": null}
, will succeed with None
{"world": 123}
, will succeed with None
["a", "list", "of", "strings"]
, will succeed with None
nullable d
will succeed with None
if the JSON value is null
. If the JSON value is non-null
, it wraps the result of running x
in a Some
.
For example, field "hello" (nullable int)
:
{"hello": 123}
, will succeed with Some 123
{"hello": null}
, will succeed with None
{"world": 123}
, will fail["a", "list", "of", "strings"]
, will failpick choices
picks a single choice, like one_of
. However, each element of choices
can look at the value, decide if it applies (e.g. based on the value of a single field, like a "kind" or "type" field), and if it does, returns a decoder for the rest of the value.
If a choice is made, even if the returned sub-decoder fails, the error message will totally ignore the rest of the choices and only be about the choice that was initially made.
decode_sub value sub_dec
uses sub_dec
to decode value
. This is useful when one has a value on hand.
Try two decoders and then combine the result. We can use this to decode objects with many fields (but it's preferable to use Infix.(>>=)
- see the README).
val keys : string list decoder
Decode all of the keys of an object to a list of strings.
Decode an object into a list of key-value pairs.
Decode an object into a list of values, where the value decoder depends on the key.
keys'
is for when your keys might not be strings - probably only likely for Yaml.
val succeed : 'a -> 'a decoder
A decoder that always succeeds with the argument, ignoring the input.
val fail : string -> 'a decoder
A decoder that always fails with the given message, ignoring the input.
val from_result : ( 'a, error ) Decoders.Util.My_result.t -> 'a decoder
Create decoders that depend on previous results.
Recursive decoders.
let my_decoder = fix (fun my_decoder -> ...)
allows you to define my_decoder
in terms of itself.
val of_of_string : msg:string -> ( string -> 'a option ) -> 'a decoder
Create a decoder from a function of_string : string -> 'a option
module Infix : sig ... end
include module type of Infix
include module type of Decoders.Decoder.Infix
val (>>=) :
( 'i -> ( 'a, 'i Decoders.Error.t ) Stdlib.result ) ->
( 'a -> 'i -> ( 'b, 'i Decoders.Error.t ) Stdlib.result ) ->
'i ->
( 'b, 'i Decoders.Error.t ) Stdlib.result
val (>|=) :
( 'i -> ( 'a, 'i Decoders.Error.t ) Stdlib.result ) ->
( 'a -> 'b ) ->
'i ->
( 'b, 'i Decoders.Error.t ) Stdlib.result
val (<*>) :
( 'i -> ( 'a -> 'b, 'i Decoders.Error.t ) Stdlib.result ) ->
( 'i -> ( 'a, 'i Decoders.Error.t ) Stdlib.result ) ->
'i ->
( 'b, 'i Decoders.Error.t ) Stdlib.result
type ('i, 'o) t_let = 'i -> ( 'o, 'i Decoders.Error.t ) Stdlib.result
val decode_value :
'a decoder ->
value ->
( 'a, error ) Decoders.Util.My_result.t
Run a decoder on some input.
val decode_string :
'a decoder ->
string ->
( 'a, error ) Decoders.Util.My_result.t
Run a decoder on a string.
val decode_file :
'a decoder ->
string ->
( 'a, error ) Decoders.Util.My_result.t
Run a decoder on a file.
module Pipeline : sig ... end