# (* preparation *);;
# open Kxclib;;
# open Doctests_utils.Test_utils;;
Bindoj.Caml_gen.Json_codec.gen_encoder
/Bindoj.Caml_gen.Json_codec.gen_decoder
are functions generating JSON encoder/decoder from
Bindoj.Type_desc.type_declaration
. The following are JSON encoder/decoder
generation examples: student
of record type and person
of variant type.
# open Bindoj.Type_desc;;
# #show_type type_decl;;
type nonrec type_decl =
type_decl = {
td_name : string;
td_configs : [ `type_decl ] configs;
td_kind : type_decl_kind;
td_doc : Bindoj_base.Runtime.doc;
}
# let student_desc =
record_decl "student" [
record_field "admission_year" (Coretype.mk_prim `int);
record_field "full_name" (Coretype.mk_prim `string);
];;
val student_desc : type_decl =
{td_name = "student"; td_configs = Bindoj.Type_desc.Configs.[];
td_kind =
Record_decl
[{rf_name = "admission_year";
rf_type =
`direct
{Bindoj.Type_desc.Coretype.ct_desc =
Bindoj.Type_desc.Coretype.Prim `int;
ct_configs = Bindoj_typedesc.Type_desc.Configs.[]};
rf_configs = Bindoj.Type_desc.Configs.[]; rf_doc = `nodoc};
{rf_name = "full_name";
rf_type =
`direct
{Bindoj.Type_desc.Coretype.ct_desc =
Bindoj.Type_desc.Coretype.Prim `string;
ct_configs = Bindoj_typedesc.Type_desc.Configs.[]};
rf_configs = Bindoj.Type_desc.Configs.[]; rf_doc = `nodoc}];
td_doc = `nodoc}
# open Bindoj.Caml_gen.Json_codec;;
# #show_type type_decl;;
type nonrec type_decl =
type_decl = {
td_name : string;
td_configs : [ `type_decl ] configs;
td_kind : type_decl_kind;
td_doc : Bindoj_runtime.doc;
}
# let person_desc =
variant_decl "person" [
variant_constructor "Anonymous" `no_param;
variant_constructor "With_id" (`tuple_like [variant_argument & Coretype.mk_prim `int]);
variant_constructor "Student" (`inline_record [
record_field "student_id" (Coretype.mk_prim `int);
record_field "name" (Coretype.mk_prim `string);
]);
variant_constructor "Teacher" (`inline_record [
record_field "faculty_id" (Coretype.mk_prim `int);
record_field "name" (Coretype.mk_prim `string);
record_field "department" (Coretype.mk_prim `string);
])
];;
val person_desc : type_decl =
{td_name = "person"; td_configs = Bindoj.Type_desc.Configs.[];
td_kind =
Variant_decl
[{vc_name = "Anonymous"; vc_param = `no_param;
vc_configs = Bindoj.Type_desc.Configs.[]; vc_doc = `nodoc};
{vc_name = "With_id";
vc_param =
`tuple_like
[{va_type =
`direct
{Bindoj.Type_desc.Coretype.ct_desc =
Bindoj.Type_desc.Coretype.Prim `int;
ct_configs = Bindoj_typedesc.Type_desc.Configs.[]};
va_configs = Bindoj.Type_desc.Configs.[]; va_doc = `nodoc}];
vc_configs = Bindoj.Type_desc.Configs.[]; vc_doc = `nodoc};
{vc_name = "Student";
vc_param =
`inline_record
[{rf_name = "student_id";
rf_type =
`direct
{Bindoj.Type_desc.Coretype.ct_desc =
Bindoj.Type_desc.Coretype.Prim `int;
ct_configs = Bindoj_typedesc.Type_desc.Configs.[]};
rf_configs = Bindoj.Type_desc.Configs.[]; rf_doc = `nodoc};
{rf_name = "name";
rf_type =
`direct
{Bindoj.Type_desc.Coretype.ct_desc =
Bindoj.Type_desc.Coretype.Prim `string;
ct_configs = Bindoj_typedesc.Type_desc.Configs.[]};
rf_configs = Bindoj.Type_desc.Configs.[]; rf_doc = `nodoc}];
vc_configs = Bindoj.Type_desc.Configs.[]; vc_doc = `nodoc};
{vc_name = "Teacher";
vc_param =
`inline_record
[{rf_name = "faculty_id";
rf_type =
`direct
{Bindoj.Type_desc.Coretype.ct_desc =
Bindoj.Type_desc.Coretype.Prim `int;
ct_configs = Bindoj_typedesc.Type_desc.Configs.[]};
rf_configs = Bindoj.Type_desc.Configs.[]; rf_doc = `nodoc};
{rf_name = "name";
rf_type =
`direct
{Bindoj.Type_desc.Coretype.ct_desc =
Bindoj.Type_desc.Coretype.Prim `string;
ct_configs = Bindoj_typedesc.Type_desc.Configs.[]};
rf_configs = Bindoj.Type_desc.Configs.[]; rf_doc = `nodoc};
{rf_name = "department";
rf_type =
`direct
{Bindoj.Type_desc.Coretype.ct_desc =
Bindoj.Type_desc.Coretype.Prim `string;
ct_configs = Bindoj_typedesc.Type_desc.Configs.[]};
rf_configs = Bindoj.Type_desc.Configs.[]; rf_doc = `nodoc}];
vc_configs = Bindoj.Type_desc.Configs.[]; vc_doc = `nodoc}];
td_doc = `nodoc}
# Bindoj.(
let student_encoder = Caml_gen.Json_codec.gen_json_encoder student_desc in
Caml.Structure.([binding student_encoder] |> printf "%a@?" pp_caml));;
let student_to_json =
(fun { admission_year = x0; full_name = x1 } ->
`obj
[("admissionYear", (int_to_json x0));
("fullName", (string_to_json x1))] : student -> Kxclib.Json.jv)
[@@warning "-39"]
- : unit = ()
# Bindoj.(
let student_decoder = Caml_gen.Json_codec.gen_json_decoder_result student_desc in
Caml.Structure.([binding student_decoder] |> printf "%a@?" pp_caml));;
let student_of_json' =
(fun ?(path= []) ->
fun x ->
((let rec of_json_impl path __bindoj_orig =
match __bindoj_orig with
| `obj param ->
let (>>=) = Result.bind in
(((List.assoc_opt "admissionYear" param) |>
(Option.to_result
~none:("mandatory field 'admissionYear' does not exist",
path)))
>>= (int_of_json' ((`f "admissionYear") :: path)))
>>=
((fun x0 ->
(((List.assoc_opt "fullName" param) |>
(Option.to_result
~none:("mandatory field 'fullName' does not exist",
path)))
>>= (string_of_json' ((`f "fullName") :: path)))
>>=
(fun x1 -> Ok { admission_year = x0; full_name = x1 })))
| jv ->
Error
((Printf.sprintf
"an object is expected for a record value, but the given is of type '%s'"
(let open Kxclib.Json in
string_of_jv_kind (classify_jv jv))), path) in
of_json_impl) path x) |>
(Result.map_error
(fun (msg, path) ->
(msg, path,
(`with_warning
("not considering any config if exists",
(`named
("Student",
(`object_of
[`mandatory_field ("admissionYear", `integral);
`mandatory_field ("fullName", `string)])))))))) :
student Bindoj_runtime.json_full_decoder)[@@warning "-39"]
- : unit = ()
# Bindoj.(
let person_encoder = Caml_gen.Json_codec.gen_json_encoder person_desc in
Caml.Structure.([binding person_encoder] |> printf "%a@?" pp_caml));;
let person_to_json =
(function
| Anonymous -> `obj [("kind", (`str "anonymous"))]
| With_id x0 ->
`obj [("kind", (`str "with-id")); ("value", (int_to_json x0))]
| Student { student_id = x0; name = x1 } ->
`obj
[("kind", (`str "student"));
("studentId", (int_to_json x0));
("name", (string_to_json x1))]
| Teacher { faculty_id = x0; name = x1; department = x2 } ->
`obj
[("kind", (`str "teacher"));
("facultyId", (int_to_json x0));
("name", (string_to_json x1));
("department", (string_to_json x2))] : person -> Kxclib.Json.jv)
[@@warning "-39"]
- : unit = ()
# Bindoj.(
let person_decoder = Caml_gen.Json_codec.gen_json_decoder_result person_desc in
Caml.Structure.([binding person_decoder] |> printf "%a@?" pp_caml));;
let person_of_json' =
(fun ?(path= []) ->
fun x ->
((let rec of_json_impl path __bindoj_orig =
match Kxclib.Jv.pump_field "kind" __bindoj_orig with
| `obj (("kind", `str "anonymous")::_) -> Ok Anonymous
| `obj (("kind", `str "with-id")::param) ->
(match List.assoc_opt "value" param with
| Some arg ->
let (>>=) = Result.bind in
(int_of_json' ((`f "value") :: path) arg) >>=
((fun x0 -> Ok (With_id x0)))
| None ->
Error ("mandatory field 'value' does not exist", path))
| `obj (("kind", `str "student")::param) ->
let (>>=) = Result.bind in
(((List.assoc_opt "studentId" param) |>
(Option.to_result
~none:("mandatory field 'studentId' does not exist",
path)))
>>= (int_of_json' ((`f "studentId") :: path)))
>>=
((fun x0 ->
(((List.assoc_opt "name" param) |>
(Option.to_result
~none:("mandatory field 'name' does not exist",
path)))
>>= (string_of_json' ((`f "name") :: path)))
>>=
(fun x1 -> Ok (Student { student_id = x0; name = x1 }))))
| `obj (("kind", `str "teacher")::param) ->
let (>>=) = Result.bind in
(((List.assoc_opt "facultyId" param) |>
(Option.to_result
~none:("mandatory field 'facultyId' does not exist",
path)))
>>= (int_of_json' ((`f "facultyId") :: path)))
>>=
((fun x0 ->
(((List.assoc_opt "name" param) |>
(Option.to_result
~none:("mandatory field 'name' does not exist",
path)))
>>= (string_of_json' ((`f "name") :: path)))
>>=
(fun x1 ->
(((List.assoc_opt "department" param) |>
(Option.to_result
~none:("mandatory field 'department' does not exist",
path)))
>>=
(string_of_json' ((`f "department") :: path)))
>>=
(fun x2 ->
Ok
(Teacher
{
faculty_id = x0;
name = x1;
department = x2
})))))
| `obj (("kind", `str discriminator_value)::_) ->
Error
((Printf.sprintf
"given discriminator field value '%s' is not one of [ 'anonymous', 'with-id', 'student', 'teacher' ]"
discriminator_value), ((`f "kind") :: path))
| `obj (("kind", jv)::_) ->
Error
((Printf.sprintf
"a string is expected for a variant discriminator, but the given is of type '%s'"
(let open Kxclib.Json in
string_of_jv_kind (classify_jv jv))), ((`f "kind") ::
path))
| `obj _ ->
Error ("discriminator field 'kind' does not exist", path)
| jv ->
Error
((Printf.sprintf
"an object is expected for a variant value, but the given is of type '%s'"
(let open Kxclib.Json in
string_of_jv_kind (classify_jv jv))), path) in
of_json_impl) path x) |>
(Result.map_error
(fun (msg, path) ->
(msg, path,
(`with_warning
("not considering any config if exists",
(`named
("Person",
(`anyone_of
[`object_of
[`mandatory_field
("kind", (`exactly (`str "anonymous")))];
`object_of
[`mandatory_field
("kind", (`exactly (`str "with-id")));
`mandatory_field ("value", `integral)];
`object_of
[`mandatory_field
("kind", (`exactly (`str "student")));
`mandatory_field ("studentId", `integral);
`mandatory_field ("name", `string)];
`object_of
[`mandatory_field
("kind", (`exactly (`str "teacher")));
`mandatory_field ("facultyId", `integral);
`mandatory_field ("name", `string);
`mandatory_field ("department", `string)]])))))))) :
person Bindoj_runtime.json_full_decoder)[@@warning "-39"]
- : unit = ()