aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Letan <lthms@soap.coffee>2022-08-21 15:44:03 +0200
committerThomas Letan <lthms@soap.coffee>2022-08-21 15:46:01 +0200
commit72733c882301f4cdd9990c15d4396d2b0a508bc4 (patch)
tree05ffa407e21f92d2e4a1ebd14dac94c1ec3ae8cd
parentTurn Json_decoder.of_string exception-safe (diff)
Document Json_decoder module and correctly license it
-rw-r--r--lib/json_decoder/json_decoder.ml4
-rw-r--r--lib/json_decoder/json_decoder.mli106
2 files changed, 110 insertions, 0 deletions
diff --git a/lib/json_decoder/json_decoder.ml b/lib/json_decoder/json_decoder.ml
index acedc76..7953574 100644
--- a/lib/json_decoder/json_decoder.ml
+++ b/lib/json_decoder/json_decoder.ml
@@ -1,3 +1,7 @@
+(* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. *)
+
type 'a t = Ezjsonm.value -> 'a
module Syntax = struct
diff --git a/lib/json_decoder/json_decoder.mli b/lib/json_decoder/json_decoder.mli
new file mode 100644
index 0000000..81fda49
--- /dev/null
+++ b/lib/json_decoder/json_decoder.mli
@@ -0,0 +1,106 @@
+(* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. *)
+
+type 'a t
+(** A Json decoder for a value of type ['a].
+
+ Note that it is possible to provide a Json value that is a
+ superset of what a decoder requires. *)
+
+val of_string : 'a t -> string -> 'a option
+(** [of_string enc input] interprets [input] as a serialized Json
+ value, then uses [enc] to decode it into an OCaml value, if
+ possible. It returns [None] in case of error. *)
+
+val of_string_exn : 'a t -> string -> 'a
+(** Same as [of_string], but raises exceptions in case of error. *)
+
+(** This modules provides a monadic interface to compose existing
+ decoders together. *)
+module Syntax : sig
+ val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
+ (** The [bind] operator. *)
+
+ val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
+ (** The [map] operator. *)
+
+ val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t
+
+ val return : 'a -> 'a t
+ (** [return x] is the decoder that ignores the input Json value, and
+ always return [x]. *)
+end
+
+val field : string -> 'a t -> 'a t
+(** [field name dec] decodes the input Json as an object which
+ contains at least one property whose name is [name], and whose
+ value can be decoded with [dec]. The resulting OCaml value is
+ returned as-is.
+
+ [field] is typically used to read from several property of an
+ object, which can later be composed together.
+
+ {[
+ let tup2 dl dr =
+ let open Json_decoder.Syntax in
+ let+ x = field "0" dl
+ and+ y = field "1" dr in
+ (x, y)
+ ]}
+
+ The decoding will fail in the input Json value does not have a
+ property [name]. *)
+
+val field_opt : string -> 'a t -> 'a option t
+(** Same as {!field}, but the the decoding will not fail if the input
+ Json value does not have the expected property. In that case,
+ [None] is returned. *)
+
+val list : 'a t -> 'a list t
+(** [list enc] decodes the input Json value as a list of values which
+ can be decoded using [enc]. *)
+
+val string : string t
+(** [string] decodes the input Json value as a string. *)
+
+val int64 : int64 t
+(** [int64] decodes the input Json value as an 64-byte integer. *)
+
+val bool : bool t
+(** [bool] decodes the input Json value as a boolean. *)
+
+val float : float t
+(** [float] decodes the input Json value as a float. *)
+
+val string_enum : (string * 'a) list -> 'a t
+(** [string_enum l] decodes the input Json value as a string, then
+ compare said string with the values contained in the associated
+ list [l], to return the OCaml value associated to that string. *)
+
+val mu : ('a t -> 'a t) -> 'a t
+(** [mu] is a combinator that lets you write a recursive decoder,
+ without having to write a recursive function. For instance, if you
+ manipulate an object containing a list of similar objects
+ (typically, a tree), then you can use [mu] to express that.
+
+ {[
+ type tree = { value : int64; children : tree list }
+
+ let decoder =
+ let open Json_decoder in
+ let open Syntax in
+ mu (fun tree_decoder ->
+ let+ value = field "value" int64
+ and+ children = field "children" @@ list tree_decoder in
+ { value; children })
+ ]}
+
+ Here [decoder] will behave as expected.
+
+ {[
+ # Json_decoder.of_string decoder "{ \"value\": 3 , \"children\" : [] }" ;;
+ - : tree option = Some {value = 3L; children = []}
+ # Json_decoder.of_string decoder "{ \"value\": 3 , \"children\" : [ { \"value\": 5, \"children\": [] } ] }" ;;
+ - : tree option = Some {value = 3L; children = [{value = 5L; children = []}]}
+ ]}*)