Skip to content

Commit 6cb37fd

Browse files
Merge pull request #157 from cuihtlauac/add_path
Add `path` function
2 parents 9fc32d9 + 39877b4 commit 6cb37fd

File tree

7 files changed

+45
-1
lines changed

7 files changed

+45
-1
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
- Add Yojson.Raw.Util module to provide combinators for extracting fields
66
from Yojson.Raw values. (@tmcgilchrist, #163)
77

8+
- Add `Util.path` function to recurse into an object through a list of keys.
9+
(@cuihtlauac, @Leonidas-from-XIV, #157)
10+
811
### Removed
912

1013
### Changed

lib/util.ml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ let member name = function
2828
| `Assoc obj -> assoc name obj
2929
| js -> typerr ("Can't get member '" ^ name ^ "' of non-object type ") js
3030

31+
let rec path l obj =
32+
match l with
33+
| [] -> Some obj
34+
| key :: l -> (
35+
match obj with
36+
| `Assoc assoc -> (
37+
match List.assoc key assoc with
38+
| obj -> path l obj
39+
| exception Not_found -> None)
40+
| _ -> None)
41+
3142
let index i = function
3243
| `List l as js ->
3344
let len = List.length l in

lib/util.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,10 @@ val member : string -> t -> t
8585
object [obj], or [`Null] if [k] is not present in [obj].
8686
@raise Type_error if [obj] is not a JSON object. *)
8787

88+
val path : string list -> t -> t option
89+
(* [path l obj] recurses the JSON object [obj] for each key in the path
90+
[l] until the path is empty or there is no such key in the chain. *)
91+
8892
val index : int -> t -> t
8993
(** [index i arr] returns the value at index [i] in the JSON array [arr].
9094
Negative indices count from the end of the list (so -1 is the last

test/fixtures.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ let json_value =
88
("float", `Float 0.);
99
("string", `String "string");
1010
("list", `List [ `Int 0; `Int 1; `Int 2 ]);
11+
("assoc", `Assoc [ ("value", `Int 42) ]);
1112
]
1213

1314
let json_string =
1415
"{" ^ {|"null":null,|} ^ {|"bool":true,|} ^ {|"int":0,|}
1516
^ {|"intlit":10000000000000000000,|} ^ {|"float":0.0,|}
16-
^ {|"string":"string",|} ^ {|"list":[0,1,2]|} ^ "}"
17+
^ {|"string":"string",|} ^ {|"list":[0,1,2],|} ^ {|"assoc":{"value":42}|}
18+
^ "}"
1719

1820
let unquoted_json = {|{foo: null}|}
1921
let unquoted_value = `Assoc [ ("foo", `Null) ]

test/test.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ let () =
44
("equality", Test_monomorphic.equality);
55
("read", Test_read.single_json);
66
("write", Test_write.single_json);
7+
("util", Test_util.tests);
78
]

test/test_util.ml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
let path_empty () =
2+
Alcotest.(check (option Testable.yojson))
3+
__LOC__ (Some Fixtures.json_value)
4+
(Yojson.Safe.Util.path [] Fixtures.json_value)
5+
6+
let path_missing () =
7+
Alcotest.(check (option Testable.yojson))
8+
__LOC__ None
9+
(Yojson.Safe.Util.path [ "does not exist" ] Fixtures.json_value)
10+
11+
let path_traverse () =
12+
Alcotest.(check (option Testable.yojson))
13+
__LOC__
14+
(Some (`Int 42))
15+
(Yojson.Safe.Util.path [ "assoc"; "value" ] Fixtures.json_value)
16+
17+
let tests =
18+
[
19+
("empty path", `Quick, path_empty);
20+
("non-existing path", `Quick, path_missing);
21+
("traversal", `Quick, path_traverse);
22+
]

test/test_util.mli

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
val tests : unit Alcotest.test_case list

0 commit comments

Comments
 (0)