Json in OCaml

Our program demonstrates JSON encoding and decoding in OCaml, including handling of basic data types and custom data structures.

open Yojson.Basic.Util

(* We'll use these two record types to demonstrate encoding and
   decoding of custom types below. *)
type response1 = {
  page: int;
  fruits: string list;
}

(* In OCaml, all record fields are exported by default. *)
type response2 = {
  page: int;
  fruits: string list;
}

let () =
  (* First we'll look at encoding basic data types to
     JSON strings. Here are some examples for atomic values. *)
  let bool_json = `Bool true |> Yojson.Basic.to_string in
  print_endline bool_json;

  let int_json = `Int 1 |> Yojson.Basic.to_string in
  print_endline int_json;

  let float_json = `Float 2.34 |> Yojson.Basic.to_string in
  print_endline float_json;

  let string_json = `String "gopher" |> Yojson.Basic.to_string in
  print_endline string_json;

  (* And here are some for lists and associative lists, which encode
     to JSON arrays and objects as you'd expect. *)
  let list_json = `List [`String "apple"; `String "peach"; `String "pear"] |> Yojson.Basic.to_string in
  print_endline list_json;

  let assoc_json = `Assoc [("apple", `Int 5); ("lettuce", `Int 7)] |> Yojson.Basic.to_string in
  print_endline assoc_json;

  (* The Yojson library can automatically encode your
     custom data types. *)
  let res1 = {
    page = 1;
    fruits = ["apple"; "peach"; "pear"];
  } in
  let res1_json = `Assoc [
    ("page", `Int res1.page);
    ("fruits", `List (List.map (fun s -> `String s) res1.fruits))
  ] |> Yojson.Basic.to_string in
  print_endline res1_json;

  (* The encoding is the same for response2 since all fields are exported by default in OCaml *)
  let res2 = {
    page = 1;
    fruits = ["apple"; "peach"; "pear"];
  } in
  let res2_json = `Assoc [
    ("page", `Int res2.page);
    ("fruits", `List (List.map (fun s -> `String s) res2.fruits))
  ] |> Yojson.Basic.to_string in
  print_endline res2_json;

  (* Now let's look at decoding JSON data into OCaml values. 
     Here's an example for a generic data structure. *)
  let json_str = {|{"num":6.13,"strs":["a","b"]}|} in
  let json = Yojson.Basic.from_string json_str in

  (* We can access the values in the decoded JSON using utility functions. *)
  let num = json |> member "num" |> to_float in
  Printf.printf "%.2f\n" num;

  (* Accessing nested data requires a series of operations. *)
  let strs = json |> member "strs" |> to_list |> filter_string in
  let str1 = List.hd strs in
  print_endline str1;

  (* We can also decode JSON into custom data types. *)
  let json_str = {|{"page": 1, "fruits": ["apple", "peach"]}|} in
  let json = Yojson.Basic.from_string json_str in
  let res = {
    page = json |> member "page" |> to_int;
    fruits = json |> member "fruits" |> to_list |> filter_string;
  } in
  Printf.printf "page: %d, first fruit: %s\n" res.page (List.hd res.fruits);

  (* In OCaml, we typically write to channels like stdout directly. *)
  let oc = stdout in
  let json = `Assoc [("apple", `Int 5); ("lettuce", `Int 7)] in
  Yojson.Basic.to_channel oc json;
  print_newline ()

To run this program, you’ll need to install the yojson library using OPAM:

$ opam install yojson

Then compile and run the program:

$ ocamlc -o json_example json_example.ml -package yojson
$ ./json_example

This will output:

true
1
2.34
"gopher"
["apple","peach","pear"]
{"apple":5,"lettuce":7}
{"page":1,"fruits":["apple","peach","pear"]}
{"page":1,"fruits":["apple","peach","pear"]}
6.13
a
page: 1, first fruit: apple
{"apple":5,"lettuce":7}

We’ve covered the basics of JSON in OCaml here, but check out the Yojson documentation for more detailed information on working with JSON in OCaml.