Skip to content

Commit

Permalink
(data) Cookbook deserialise and post-process YAML recipes (#2372)
Browse files Browse the repository at this point in the history
* Deserialise and post-process recipes

* add missing verb, other minor line editing

* verb agreement, other line editing

* fixed missed verb agreement & typo

* editing

---------

Co-authored-by: Cuihtlauac ALVARADO <[email protected]>
Co-authored-by: Christine Rose <[email protected]>
Co-authored-by: sabine <[email protected]>
  • Loading branch information
4 people committed Oct 17, 2024
1 parent c4180a1 commit 800ea86
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 0 deletions.
78 changes: 78 additions & 0 deletions data/cookbook/deserialise-post-process-from-yaml/00-yaml.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
packages:
- name: yaml
tested_version: 3.2.0
used_libraries:
- yaml
- name: ppx_deriving_yaml
tested_version: 0.2.2
used_libraries:
- ppx_deriving_yaml
---

(* This YAML string contains a list of ingredients where the ingredients are represented as
a YAML object, with keys representing names and values representing amounts. *)
let yaml_string = {|
french name: pâte sucrée
ingredients:
- flour: 250
- butter: 100
- sugar: 100
- egg: 50
- salt: 5
steps:
- soften butter
- add sugar
- add egg and salt
- add flour
|}

(* The `[@@deriving of_yaml]` attribute makes the `ppx_deriving_yaml` library generate the function
``ingredient_of_yaml : Yaml.value -> (ingredient, [> `Msg of string]) result``. *)
type ingredient = {
name: string;
weight: int;
} [@@deriving of_yaml]

(* The `[@@deriving of_yaml]` attribute makes the `ppx_deriving_yaml` library generate the function
``recipe_of_yaml : Yaml.value -> (ingredient, [> `Msg of string]) result``. *)
type recipe = {
name: string; [@key "french name"]
ingredients: ingredient list;
steps: string list;
} [@@deriving of_yaml]

(* Since the structure of the YAML file does not exactly match the `recipe` type,
we (1) parse the YAML file to the internal representation `Yaml.value` of the `yaml` package,
and then (2) change the structure to match the `recipe` type, so we can use the `recipe_of_yaml`
function.
The functions `add_keys` and `at_ingredients` perform this post-processing. *)
let add_keys = function
| `O [(name, `Float weight)] ->
`O [
("name", `String name);
("weight", `Float weight);
]
| v -> v

let at_ingredients f = function
| `O [
("french name", `String name);
("ingredients", `A ingredients);
("steps", `A steps)
] -> `O [
("french name", `String name);
("ingredients",
Yaml.Util.map_exn f (`A ingredients));
("steps", `A steps);
]
| v -> v

(* Parse, post-process, and convert the YAML string into an OCaml value of type `recipe`. *)
let pate_sucree =
yaml_string
|> Yaml.of_string
|> Result.map (at_ingredients add_keys)
|> fun yaml -> Result.bind yaml recipe_of_yaml

79 changes: 79 additions & 0 deletions data/cookbook/deserialise-post-process-from-yaml/01-hl_yaml.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
packages:
- name: hl_yaml
tested_version: 1.0.0
used_libraries:
- hl_yaml
- name: ppx_deriving_yojson
tested_version: 3.7.0
used_libraries:
- ppx_deriving_yojson
---

(* This YAML string contains a list of ingredients where the ingredients are represented as
a YAML object, with keys representing names and values representing amounts. *)
let yaml = {|
french name: pâte sucrée
ingredients:
- flour: 250
- butter: 100
- sugar: 100
- egg: 50
- salt: 5
steps:
- soften butter
- add sugar
- add egg and salt
- add flour
|}

(* The `[@@deriving of_yojson]` attribute makes the `ppx_deriving_yojson` library generate the function
`ingredient_of_yojson : Yojson.Safe.t -> (ingredient, string) result`. *)
type ingredient = {
name: string;
weight: int;
} [@@deriving of_yojson]

(* The `[@@deriving of_yojson]` attribute makes the `ppx_deriving_yojson` library generate the function
``recipe_of_yojson : Yojson.Safe.t -> (ingredient, string) result``. *)
type recipe = {
name: string; [@key "french name"]
ingredients: ingredient list;
steps: string list;
} [@@deriving of_yojson]

(* Since the structure of the YAML file does not exactly match the `recipe` type,
we (1) parse the YAML file to the representation `Yojson.Safe.t`,
and then (2) modify the `Yojson.Safe.t` value to change the structure to match the `recipe` type,
so we can use the `recipe_of_yojson` function.
The functions `add_keys` and `at_ingredients` perform this post-processing. *)
let add_keys = function
| `Assoc [(name, `Int weight)] ->
`Assoc [
("name", `String name);
("weight", `Int weight);
]
| v -> v

let at_ingredients f = function
| `Assoc [
("french name", `String name);
("ingredients", `List ingredients);
("steps", `List steps)
] -> `Assoc [
("french name", `String name);
("ingredients",
Yojson.Safe.Util.map f (`List ingredients));
("steps", `List steps);
]
| v -> v

(* Parse, post-process, and convert the YAML string into an OCaml value of type `recipe`. *)
let pate_sucree =
let of_yojson json =
json
|> at_ingredients add_keys
|> recipe_of_yojson in
yaml
|> Hl_yaml.Unix.parse ~of_yojson
4 changes: 4 additions & 0 deletions data/cookbook/tasks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,12 @@ categories:
slug: serialise-to-yaml
- title: Deserialise YAML Data
slug: deserialise-from-yaml
description: |
Deserialise a YAML file whose structure maps to some OCaml types.
- title: Deserialise and Post-Process YAML Data
slug: deserialise-post-process-from-yaml
description: |
Deserialise a YAML file whose structure does not directly map to some OCaml types.
- title: Date and Time
tasks:
- title: Get Today's Date
Expand Down

0 comments on commit 800ea86

Please sign in to comment.