podington
podington

Reputation: 179

How do you iterate through the keys/members in a JSON object using Yojson?

The only examples I can see so far about processing json objects involve knowing the key beforehand, but how would I be able to go through the keys/members and process their values individually?

Upvotes: 1

Views: 1040

Answers (2)

Martin Jambon
Martin Jambon

Reputation: 4939

Yojson was designed to serve as the runtime for atdgen. Atdgen saves users a lot of time by translating JSON data into data of your OCaml types and vice-versa.

The solution for JSON objects representing association lists is the following (sample source file assoc.atd):

type t = (string * foo) list <json repr="object">

type foo = {
  x: int;
  y: int;
}

Sample data of type t in JSON format:

{
  "p1": { "x": 1, "y": 2 },
  "p2": { "x": 14, "y": 0 },
  "q": { "x": 9, "y": -1 }
}

The generated interface for this example is:

(* This is file assoc_j.mli derived from assoc.atd.
   It provides the serializers and deserializers for JSON.
   Other files are generated for other purposes,
   including assoc_t.mli which contains only the OCaml
   type definitions.
 *)

type foo = Assoc_t.foo = { x: int; y: int }

type t = Assoc_t.t

val write_foo :
  Bi_outbuf.t -> foo -> unit
  (** Output a JSON value of type {!foo}. *)

val string_of_foo :
  ?len:int -> foo -> string
  (** Serialize a value of type {!foo}
      into a JSON string.
      @param len specifies the initial length
                 of the buffer used internally.
                 Default: 1024. *)

val read_foo :
  Yojson.Safe.lexer_state -> Lexing.lexbuf -> foo
  (** Input JSON data of type {!foo}. *)

val foo_of_string :
  string -> foo
  (** Deserialize JSON data of type {!foo}. *)

val write_t :
  Bi_outbuf.t -> t -> unit
  (** Output a JSON value of type {!t}. *)

val string_of_t :
  ?len:int -> t -> string
  (** Serialize a value of type {!t}
      into a JSON string.
      @param len specifies the initial length
                 of the buffer used internally.
                 Default: 1024. *)

val read_t :
  Yojson.Safe.lexer_state -> Lexing.lexbuf -> t
  (** Input JSON data of type {!t}. *)

val t_of_string :
  string -> t
  (** Deserialize JSON data of type {!t}. *)

Upvotes: 2

Steve Vinoski
Steve Vinoski

Reputation: 20014

Assuming you've created a json instance, perhaps by reading from a file using Yojson.Basic.from_channel (open_in filename) or from a string using Yojson.Basic.from_string string, you could then convert it to an association list using Yojson.Basic.Util.to_assoc and recursively iterate over it like this:

open Yojson.Basic

let iter js =
  let rec f (nm,j) =
    Printf.printf "\"%s\": " nm;
    match j with
    | `Assoc a ->
       Printf.printf "assoc\n";
       List.iter f a
    | `Bool b ->
       Printf.printf "bool: %b\n" b
    | `Float f ->
       Printf.printf "float: %f\n" f
    | `Int i ->
       Printf.printf "int: %d\n" i
    | `List jl ->
       Printf.printf "list: [";
       List.iter (List.iter f) (List.map Util.to_assoc jl);
       Printf.printf "]\n"
    | `Null ->
       Printf.printf "null\n"
    | `String s ->
       Printf.printf "string: \"%s\"\n" s in
  List.iter f (Util.to_assoc js)

This iter function first converts its js argument to an association list and then iterates over it using function f. The f function takes a tuple argument of a string and a json type. For the sake of this example it prints the string, then matches the json type and acts on it accordingly. Note how f handles the values for the `Assoc and `List variants recursively.

Upvotes: 2

Related Questions