Using Map and ppx yojson

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.