YKW
July 28, 2022, 1:13am
1
What is the best way to go from Js.Json to Belt.Map?
I need more control over the fetched object that JS.Dict does not provide
Edit: to be more clear, I’m trying to do something like this post from @johnj where the internal object is updated to have a new shape. I’d imagine this is what Belt.Map.String.update() is for.
THis is the shape of the object
type foo = Js.Dict.t<{
"role": string,
"name": string,
"inviteLink": string,
"count": int
}>
What have you tried so far?
YKW
July 28, 2022, 2:44am
3
I’m using rescript-json-combinators to decode the json
type guild = {"role": string, "name": string, "inviteLink": option<string>}
type guilds = Js.Dict.t<guild>
let guild = Json.Decode.object(field => {
"role": field.required(. "role", Json.Decode.string),
"name": field.required(. "name", Json.Decode.string),
"inviteLink": field.optional(. "inviteLink", Json.Decode.string),
})
let guilds = guild->Json.Decode.dict
This gives the type
JsonCombinators.Json.Decode.t<
Js.Dict.t<
{
"inviteLink": option<string>,
"name": string,
"role": string,
},
>,
>
There is no good way to map the returned record to a Map
I’m now trying Js.Obj.assign
which seems to be a decent workaround
YKW
July 28, 2022, 3:18am
4
I ended up using Js.Obj.assign
let replaceEntry = (~config, ~key, ~entry) => {
let {id, name, token, decoder} = config
ReadGist.content(~token, ~id, ~name, ~decoder)
->then(content => {
let prev = content->Js.Dict.get(key)->Belt.Option.getExn
let new = prev->Js.Obj.assign(entry)
content->Js.Dict.set(key, new)
let content = content->Js.Json.stringifyAny
let files = Js.Dict.empty()
files->Js.Dict.set(name, {"content": content})
let body = {
"gist_id": gistId,
"description": "Update guilds",
"files": files,
}
let params = {
"method": "PATCH",
"headers": {
"Authorization": `token ${token}`,
"Accept": "application/vnd.github+json",
},
"body": body->Js.Json.stringifyAny,
}
`https://api.github.com/gists/${gistId}`
->fetch(params)
->then(res => {
switch res->Response.status {
| 200 => Ok(200)->resolve
| status => {
res
->Response.json
->then(
json => {
Js.log2(status, json->Json.stringify)
resolve()
},
)
->ignore
Error(Response.PatchError)->resolve
}
}
})
})
->catch(e => {
Js.log2("e: ", e)
resolve(Error(e))
})
DZakh
July 28, 2022, 3:25am
5
With rescript-struct it would look like this:
type guild = {role: string, name: string, inviteLink: option<string>}
let guildStruct =
S.record3(.
("role", S.string()),
("name", S.string()),
("inviteLink", S.option(S.string())),
)->S.transform(
~parser=((role, name, inviteLink)) => {role: role, name: name, inviteLink: inviteLink}->Ok,
(),
)
let guildsStruct =
S.dict(guildStruct)->S.transform(
~parser=guildsDict => guildsDict->Js.Dict.entries->Belt.Map.String.fromArray->Ok,
(),
)
data->S.parseWith(guildsStruct)
The guildsStruct
has the type
S.t<Belt.Map.String.t<guild>>