-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert from {"type": "foo", ...} json data to variant values #47
Comments
I managed to do something with custom enc/decoders: [@decco]
type image = {url: string};
[@decco]
type text = {
title: string,
body: string,
};
type document =
| Image(image)
| Text(text);
let encoder = doc =>
switch (doc) {
| Image(img) =>
let obj = image_encode(img)->Js.Json.decodeObject->Option.getExn;
Js.Dict.set(obj, "type", "image"->Js.Json.string);
Js.Json.object_(obj);
| Text(txt) =>
let obj = text_encode(txt)->Js.Json.decodeObject->Option.getExn;
Js.Dict.set(obj, "type", "text"->Js.Json.string);
Js.Json.object_(obj);
};
let decoder = json => {
switch (json |> Js.Json.classify) {
| JSONObject(obj) =>
switch (
Js.Dict.get(obj, "type")->Option.flatMap(Js.Json.decodeString),
image_decode(json),
text_decode(json),
) {
| (Some(type_), Ok(img), _) when type_ == "image" => Image(img)->Ok
| (Some(type_), _, Ok(txt)) when type_ == "text" => Text(txt)->Ok
| _ => Error({ Decco.path: "", message: "Not a document", value: json })
}
| _ => Error({ Decco.path: "", message: "Expected JSONObject for document", value: json })
};
};
let codec: Decco.codec(document) = (encoder, decoder);
[@decco]
type t = [@decco.codec codec] document; But it's probably too convoluted... |
This is good stuff. Converting an object with a discriminator field (often called |
Interesting thought. I feel like this is along the same lines as some of the discussion in #30. In the meantime, I think your example can be simplified slightly: open Belt;
[@decco]
type discriminator = {
[@decco.key "type"]
type_: string,
};
[@decco]
type image = {
[@decco.key "type"]
type_: string,
url: string,
};
[@decco]
type text = {
[@decco.key "type"]
type_: string,
title: string,
body: string,
};
type document =
| Image(image)
| Text(text);
let encoder = doc =>
switch (doc) {
| Image(img) => image_encode(img)
| Text(txt) => text_encode(txt)
};
[@warning "-4"]
let decoder = json =>
switch (json |> discriminator_decode) {
| Ok({type_: "image"}) => image_decode(json)->Result.map(v => Image(v))
| Ok({type_: "text"}) => text_decode(json)->Result.map(v => Text(v))
| _ =>
Decco.error(
"Expected JSON object with type of ['image' | 'text']",
json,
)
};
let codec: Decco.codec(document) = (encoder, decoder);
[@decco]
type t = [@decco.codec codec] document; Another option if you want straight json->json adapters is to do something like [@decco]
type t =
| Image(image)
| Text(string);
let t_encode = (t) => t_encode(t) -> normalize;
let t_decode = (json) => t_decode(json) -> Result.map(restore); |
Is there a simple way to convert from values like
to variant values like
In atdgen, there is a directive called
ocaml.adapter
that is similar todecco.codec
but runs a bit earlier, and instead converts from Json.t to Json.t, just to do some preliminary massaging to the json values so they can be parsed properly.Maybe something like this could exist in decco:
With a provided module to convert from json values where
type
key drives the variant used:The
TypeField
module is also inspired in atdgen, which provides a similar thing out of the box: https://github.com/ahrefs/bs-atdgen-codec-runtime/blob/a40203875041e49bb78d2a46cb77f4b637865bae/src/atdgen_codec_runtime.ml#L15-L57The text was updated successfully, but these errors were encountered: