Here is the minimum example I manage to write in order to parse json with string keys and integer values into a Yojson variable, and vice versa.
Is there a way to have it working for other Yojson values (float, string, bool, …), i.e. to make it generic
module StringMap = struct
include Map.Make(String)
let of_yojson json =
let conv p = match p with
(k, `Int v) -> (k,v)
| _ -> failwith "values must be of type int" in
match json with
`Assoc l -> of_list (List.map conv l)
| _ -> failwith "Only valide for int value type"
let to_yojson map =
let conv (k,v) = (k, `Int v) in
`Assoc (List.map conv (bindings map))
end
let () =
let a = {|{"1":1,"2":2}|} in
let b = Yojson.Safe.from_string a in
let m = StringMap.of_yojson b in
let j = StringMap.to_yojson m in
print_endline (Yojson.Safe.to_string j )
Looking at what I did in Open Railroad Tycoon, I took advantage of the improved support for hashtables to make writing this stuff near-effortless.
module Map = struct
module type S = sig
include CCMap.S
val t_of_yojson : Yojson.Safe.t -> 'a t
val yojson_of_t : 'a t -> Yojson.Safe.t
end
module Make(O:OrderedType) = struct
include CCMap.Make(O)
let to_hashtbl v =
let h = Hashtbl.create 10 in
iter (fun k v -> Hashtbl.replace h k v) v;
h
let of_hashtbl h =
let m = empty in
Hashtbl.fold (fun k v m -> add k v m) h m
let t_of_yojson conv (json:Yojson.Safe.t) =
hashtbl_of_yojson O.t_of_yojson conv json |> of_hashtbl
let yojson_of_t conv v =
to_hashtbl v |> yojson_of_hashtbl O.yojson_of_t conv
end
end
module IntMap = Map.Make(struct
type t = int [@@deriving yojson]
let compare (x:int) y = x - y
end)
Note that I’m using CCMap, which is the expanded Map from Containers, but it shouldn’t make a difference.
Thank you @bluddy for the feedback. Though you define t_of_yojson and yojson_of_t as function with only one argument (Yojson.Safe.t and 'a t) in the module signature. Whereas later on these two function need conversion function conv as a first argument so the signatures don’t match I am affraid, or I am missing an important thing here ?
Your code fails at compilation and I could not fix it. hashtbl_of_yojson is missing for instance. I assume this must be produced by an [@@deriving yojson] but I could not find where in the reference you submitted.
But before solving that, I think I have to understand why it is possible to have to_yojson and of_yojson functions allow this conv function as a first parameter. I thought their signature was fixed by the yojson ppx to the signature you provided at the beginning :
val t_of_yojson : Yojson.Safe.t -> 'a t
val yojson_of_t : 'a t -> Yojson.Safe.t
Every file that makes use of ppx_yojson_conv should include open Ppx_yojson_conv_lib.Yojson_conv.Primitives. I think that handles hashtables as well.
Yep I think you found a bug. I never constrain my version of Map to this module signature, so that signature is essentially dead code. There should be another parameter there in those functions. Basically the ppx expects a parameter per type variable in the signature. But I haven’t touched this in a while so I don’t remember the details.