simdjson bindings with streaming support
1let test_quick_start () =
2 assert (Simdjsont.Validate.is_valid {|{"name": "Alice", "age": 30}|});
3 let name =
4 Simdjsont.Extract.string {|{"user": {"name": "Bob"}}|} ~pointer:"/user/name"
5 in
6 assert (name = Ok "Bob");
7 let numbers =
8 Simdjsont.Decode.decode_string Simdjsont.Decode.(list int) "[1, 2, 3]"
9 in
10 assert (numbers = Ok [ 1; 2; 3 ]);
11 let json =
12 Simdjsont.Encode.to_string Simdjsont.Decode.(list string) [ "a"; "b"; "c" ]
13 in
14 assert (json = {|["a","b","c"]|})
15
16let test_validation () =
17 assert (Simdjsont.Validate.is_valid "[1, 2, 3]");
18 assert (not (Simdjsont.Validate.is_valid "{invalid"));
19 match Simdjsont.Validate.check "null" with
20 | Ok () -> ()
21 | Error _ -> assert false
22
23let test_extraction () =
24 let json =
25 {|{
26 "users": [
27 {"id": 1, "name": "Alice", "active": true},
28 {"id": 2, "name": "Bob", "active": false}
29 ],
30 "count": 2
31 }|}
32 in
33 assert (Simdjsont.Extract.string json ~pointer:"/users/0/name" = Ok "Alice");
34 assert (Simdjsont.Extract.int json ~pointer:"/count" = Ok 2);
35 assert (Simdjsont.Extract.int json ~pointer:"/users/1/id" = Ok 2);
36 assert (Simdjsont.Extract.bool json ~pointer:"/users/0/active" = Ok true);
37 assert (
38 Simdjsont.Extract.is_null {|{"value": null}|} ~pointer:"/value" = Ok true)
39
40let test_decoding_primitives () =
41 assert (Simdjsont.Decode.decode_string Simdjsont.Decode.bool "true" = Ok true);
42 assert (Simdjsont.Decode.decode_string Simdjsont.Decode.int "42" = Ok 42);
43 assert (
44 Simdjsont.Decode.decode_string Simdjsont.Decode.string {|"hello"|}
45 = Ok "hello");
46 assert (
47 Simdjsont.Decode.decode_string Simdjsont.Decode.(list int) "[1, 2, 3, 4, 5]"
48 = Ok [ 1; 2; 3; 4; 5 ]);
49 assert (
50 Simdjsont.Decode.decode_string Simdjsont.Decode.(optional string) "null"
51 = Ok None)
52
53type point = { x : int; y : int }
54
55let point_codec =
56 let open Simdjsont.Decode in
57 Obj.field (fun x y -> { x; y })
58 |> Obj.mem "x" int ~enc:(fun p -> p.x)
59 |> Obj.mem "y" int ~enc:(fun p -> p.y)
60 |> Obj.finish
61
62let test_decoding_records () =
63 match Simdjsont.Decode.decode_string point_codec {|{"x": 10, "y": 20}|} with
64 | Ok p -> assert (p.x = 10 && p.y = 20)
65 | Error _ -> assert false
66
67type user = { id : int; name : string; email : string option }
68
69let user_codec =
70 let open Simdjsont.Decode in
71 Obj.field (fun id name email -> { id; name; email })
72 |> Obj.mem "id" int ~enc:(fun u -> u.id)
73 |> Obj.mem "name" string ~enc:(fun u -> u.name)
74 |> Obj.opt_mem "email" string ~enc:(fun u -> u.email)
75 |> Obj.finish
76
77let test_decoding_optional_fields () =
78 let json = {|{"id": 1, "name": "Alice", "email": null}|} in
79 match Simdjsont.Decode.decode_string user_codec json with
80 | Ok user ->
81 assert (user.id = 1);
82 assert (user.name = "Alice");
83 assert (user.email = None)
84 | Error _ -> assert false
85
86let test_encoding () =
87 assert (Simdjsont.Encode.to_string Simdjsont.Decode.bool true = "true");
88 assert (Simdjsont.Encode.to_string Simdjsont.Decode.int 42 = "42");
89 assert (
90 Simdjsont.Encode.to_string Simdjsont.Decode.string "hello" = {|"hello"|});
91 assert (
92 Simdjsont.Encode.to_string Simdjsont.Decode.(list int) [ 1; 2; 3 ]
93 = "[1,2,3]");
94 let p = { x = 100; y = 200 } in
95 assert (Simdjsont.Encode.to_string point_codec p = {|{"x":100,"y":200}|})
96
97type item = { name : string; price : float }
98
99let item_codec =
100 let open Simdjsont.Decode in
101 Obj.field (fun name price -> { name; price })
102 |> Obj.mem "name" string ~enc:(fun i -> i.name)
103 |> Obj.mem "price" float ~enc:(fun i -> i.price)
104 |> Obj.finish
105
106let test_extract_with_codec () =
107 let json =
108 {|{
109 "order": {
110 "items": [
111 {"name": "Book", "price": 12.99},
112 {"name": "Pen", "price": 1.50}
113 ]
114 }
115 }|}
116 in
117 match Simdjsont.Extract.at item_codec json ~pointer:"/order/items/0" with
118 | Ok item ->
119 assert (item.name = "Book");
120 assert (abs_float (item.price -. 12.99) < 0.001)
121 | Error _ -> assert false
122
123let test_dynamic_json () =
124 let open Simdjsont.Json in
125 let json =
126 Object
127 [
128 ("name", String "Alice");
129 ("age", Int 30L);
130 ("scores", Array [ Int 95L; Int 87L; Int 92L ]);
131 ("active", Bool true);
132 ]
133 in
134 let s = to_string json in
135 assert (String.length s > 0);
136 assert (String.sub s 0 1 = "{")
137
138let test_dynamic_decode () =
139 match
140 Simdjsont.Decode.decode_string Simdjsont.Decode.value {|{"key": [1, 2, 3]}|}
141 with
142 | Ok v ->
143 let s = Simdjsont.Json.to_string v in
144 assert (s = {|{"key":[1,2,3]}|})
145 | Error _ -> assert false
146
147let test_low_level () =
148 let open Simdjsont.Raw in
149 let parser = create_parser () in
150 match
151 parse_string parser {|{"users": [{"name": "Alice"}, {"name": "Bob"}]}|}
152 with
153 | Ok root -> (
154 (match at_pointer root "/users/1/name" with
155 | Ok elt -> assert (string_exn elt = "Bob")
156 | Error e -> failwith e.message);
157 match at_pointer root "/users" with
158 | Ok elt ->
159 let arr = array_exn elt in
160 let count = ref 0 in
161 array_iter
162 (fun user ->
163 let obj = object_exn user in
164 match object_find obj "name" with
165 | Ok _ -> incr count
166 | Error _ -> ())
167 arr;
168 assert (!count = 2)
169 | Error e -> failwith e.message)
170 | Error e -> failwith ("Parse error: " ^ e.message)
171
172let () =
173 Alcotest.run "readme"
174 [
175 ( "examples",
176 [
177 ("quick start", `Quick, test_quick_start);
178 ("validation", `Quick, test_validation);
179 ("extraction", `Quick, test_extraction);
180 ("decoding primitives", `Quick, test_decoding_primitives);
181 ("decoding records", `Quick, test_decoding_records);
182 ("decoding optional fields", `Quick, test_decoding_optional_fields);
183 ("encoding", `Quick, test_encoding);
184 ("extract with codec", `Quick, test_extract_with_codec);
185 ("dynamic json", `Quick, test_dynamic_json);
186 ("dynamic decode", `Quick, test_dynamic_decode);
187 ("low level", `Quick, test_low_level);
188 ] );
189 ]