Skip to content

Decoding

Profile is a named struct with a fixed JSON shape. Its nickname field uses Option<String>: the decoder maps JSON null to None and a string to Some(value). The field must still be present in the JSON object.

mod decoding.profile
@moduledoc """
`Profile` is a named struct with a fixed JSON shape. Its `nickname` field
uses `Option<String>`: the decoder maps JSON `null` to `None` and a string
to `Some(value)`. The field must still be present in the JSON object.
"""
struct {
name: String,
age: Integer,
tags: List<String>,
nickname: Option<String>
}

JSON parsing starts with an unknown Term. A Decoder<A> checks that value at runtime and returns Result<A, DecodeError>. Successful decoding produces the Gale type the caller asked for. Encoding is explicit too: Encoder<A> produces Gale’s closed Json type, so values that are not JSON cannot be encoded accidentally.

mod decoding
alias gale_std.decode
alias gale_std.json as std_json
pub fn profile_decoder() -> decode.Decoder<decoding.profile.Profile> {
fn(input) -> {
with {
name <- decode.field(input, "name", decode.string())
age <- decode.field(input, "age", decode.integer())
tags <- decode.field(input, "tags", decode.list(decode.string()))
nickname <- decode.field(input, "nickname", decode.nullable(decode.string()))
%decoding.profile.Profile{
name: name,
age: age,
tags: tags,
nickname: nickname
}
}
}
}
pub fn profile_encoder() -> std_json.Encoder<decoding.profile.Profile> {
fn(profile) -> std_json.object([
{"name", std_json.string(profile.name)},
{"age", std_json.integer(profile.age)},
{"tags", std_json.array(profile.tags, fn(tag) -> std_json.string(tag))},
{
"nickname",
std_json.nullable(profile.nickname, fn(nickname) -> std_json.string(nickname))
}
])
}
pub fn decode_profile(source: String) -> Result<decoding.profile.Profile, std_json.JsonError> {
std_json.parse(source, profile_decoder())
}
pub fn encode_profile(profile: decoding.profile.Profile) -> String {
std_json.encode_with(profile, profile_encoder())
}

gale_std.json.parse(source, profile_decoder()) separates malformed JSON with JsonSyntaxError from a valid JSON value of the wrong shape with JsonValueError. Decode errors carry DecodeField and DecodeIndex paths.

Passing an arbitrary BEAM value is a type error:

pub fn encode_pid(pid: gale_std.process.Pid<Term>) -> String {
gale_std.json.encode(pid)
}