Skip to content

Commit 127d6a6

Browse files
committed
Add JsonExtractor utility class
The extractor wraps standard boilerplate for working with JsonType structures into a utility class that makes for easier reading code when pulling values of known types from a parsed Json structure.
1 parent f6a18ef commit 127d6a6

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

json/json_extractor.pony

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
use "collections"
2+
3+
class val JsonExtractor
4+
"""
5+
Utility class for working with JSON structures.
6+
7+
Given the following JSON:
8+
```json
9+
{
10+
"environments": [
11+
{
12+
"name": "corral-env",
13+
"user": "sean",
14+
"image": "an-image:latest",
15+
"shell": "fish",
16+
"workdir": "/workspace",
17+
"workspace": "/home/sean/ponylang/corral",
18+
"mounts": [
19+
{"source":"/var/run/docker.sock","target":"/var/run/docker.sock", "type": "bind"}
20+
]
21+
}
22+
]
23+
}
24+
```
25+
26+
We can use the following code to extract our values:
27+
28+
```pony
29+
primitive Parser
30+
fun apply(json: JsonType val): Array[Environment] val ? =>
31+
recover val
32+
let envs = JsonExtractor(json)("environments")?.as_array()?
33+
let result: Array[Environment] = Array[Environment]
34+
for e in envs.values() do
35+
let obj = JsonExtractor(e).as_object()?
36+
let name = JsonExtractor(obj("name")?).as_string()?
37+
let user = JsonExtractor(obj("user")?).as_string()?
38+
let image = JsonExtractor(obj("image")?).as_string()?
39+
let shell = JsonExtractor(obj("shell")?).as_string()?
40+
let workdir = JsonExtractor(obj("workdir")?).as_string()?
41+
let workspace = JsonExtractor(obj("workspace")?).as_string()?
42+
let mounts = recover trn Array[Mount] end
43+
for i in JsonExtractor(obj("mounts")?).as_array()?.values() do
44+
let m = MountParser(i)?
45+
mounts.push(m)
46+
end
47+
48+
let environment = Environment(name, user, image, shell, workdir, workspace, consume mounts)
49+
result.push(environment)
50+
end
51+
result
52+
end
53+
54+
primitive MountParser
55+
fun apply(json: JsonType val): Mount ? =>
56+
let obj = JsonExtractor(json).as_object()?
57+
let source = JsonExtractor(obj("source")?).as_string()?
58+
let target = JsonExtractor(obj("target")?).as_string()?
59+
let mtype = JsonExtractor(obj("type")?).as_string()?
60+
61+
Mount(source, target, mtype)
62+
```
63+
64+
The JsonExtractor creates a lot of intermediate objects, but it makes the code
65+
easier to read and understand. We suggest not using it in critical paths where
66+
performance is a concern.
67+
"""
68+
let _json: JsonType val
69+
70+
new val create(json: JsonType val) =>
71+
"""
72+
Create a new JsonExtractor from a JSON structure.
73+
"""
74+
_json = json
75+
76+
fun val apply(idx_or_key: (String | USize)): JsonExtractor val ? =>
77+
"""
78+
Extract an array or object by index or key and return a new JsonExtractor.
79+
"""
80+
match (_json, idx_or_key)
81+
| (let a: JsonArray val, let idx: USize) =>
82+
JsonExtractor((a.data)(idx)?)
83+
| (let o: JsonObject val, let key: String) =>
84+
JsonExtractor((o.data)(key)?)
85+
else
86+
error
87+
end
88+
89+
fun val size(): USize ? =>
90+
"""
91+
Return the size of the JSON structure.
92+
93+
Results in an error for any structure that isn't a `JsonArray` or `JsonObject`.
94+
"""
95+
match _json
96+
| let a: JsonArray val =>
97+
a.data.size()
98+
| let o: JsonObject val =>
99+
o.data.size()
100+
else
101+
error
102+
end
103+
104+
fun val values(): Iterator[JsonType val] ? =>
105+
"""
106+
Return an iterator over the values of the JSON structure.
107+
108+
Results in an error for any structure that isn't a `JsonArray`.
109+
"""
110+
match _json
111+
| let a: JsonArray val =>
112+
a.data.values()
113+
else
114+
error
115+
end
116+
117+
fun val pairs(): Iterator[(String, JsonType val)] ? =>
118+
"""
119+
Return a pairs iterator over the values of the JSON structure.
120+
121+
Results in an error for any structure that isn't a `JsonArray`.
122+
"""
123+
match _json
124+
| let o: JsonObject val =>
125+
o.data.pairs()
126+
else
127+
error
128+
end
129+
130+
fun val as_array(): Array[JsonType] val ? =>
131+
"""
132+
Extract an Array from the JSON structure.
133+
"""
134+
match _json
135+
| let a: JsonArray val =>
136+
a.data
137+
else
138+
error
139+
end
140+
141+
fun val as_object(): Map[String, JsonType] val ? =>
142+
"""
143+
Extract a Map from the JSON structure.
144+
"""
145+
match _json
146+
| let o: JsonObject val =>
147+
o.data
148+
else
149+
error
150+
end
151+
152+
fun val as_string(): String ? =>
153+
"""
154+
Extract a String from the JSON structure.
155+
"""
156+
_json as String
157+
158+
fun val as_none(): None ? =>
159+
"""
160+
Extract a None from the JSON structure.
161+
"""
162+
_json as None
163+
164+
fun val as_f64(): F64 ? =>
165+
"""
166+
Extract a F64 from the JSON structure.
167+
"""
168+
_json as F64
169+
170+
fun val as_i64(): I64 ? =>
171+
"""
172+
Extract a I64 from the JSON structure.
173+
"""
174+
_json as I64
175+
176+
fun val as_bool(): Bool ? =>
177+
"""
178+
Extract a Bool from the JSON structure.
179+
"""
180+
_json as Bool
181+
182+
fun val as_string_or_none(): (String | None) ? =>
183+
"""
184+
Extract a String or None from the JSON structure.
185+
"""
186+
match _json
187+
| let s: String => s
188+
| let n: None => n
189+
else
190+
error
191+
end

0 commit comments

Comments
 (0)