Docs/Building applications

Working with JSON

Decode a known schema or inspect a document whose shape varies.

On this page

Use typed decoding when you know the structure your application expects. Use json.Value when fields vary or the program needs to inspect and transform an arbitrary document.

Decode into a type#

Valk
use valk.json

class Profile {
    name: String
    active: bool (true)
}

fn main() {
    let text = "{\"name\":\"Ada\"}"
    let profile = json.decode_to[Profile](text) ! {
        println("Expected a profile with a name")
        return
    }
    println(profile.name)
    println(profile.active) // true
}

Decoding checks whether the document matches Profile. A field must be present unless its type is nullable or it declares an explicit default. Here, name is required and a missing active takes the declared default.

A successfully parsed JSON document is not necessarily a valid application object. Typed decoding performs the structural conversion; your application can then check rules such as non-empty names or permitted numeric ranges.

Inspect a document dynamically#

Valk
use valk.json

fn main() {
    let document = json.decode("{\"name\":\"Ada\",\"visits\":3}") ! {
        println("Malformed JSON")
        return
    }
    println(document["name"].string) // Ada
    document.set("name", "Sam")
    println(document.encode()) // {"name":"Sam","visits":3}
}

Use bracket indexing for object keys and array indexes, such as document["name"] or document["names"][0]. .string converts the selected JSON value to text.

When a field must contain a string, use document["name"].string_value() and handle its error. This rejects missing fields and values of another type.

Encode an application value#

Valk
use valk.json

class Profile {
    name: String
    active: bool
}

fn main() {
    let profile = Profile { name: "Ada", active: true }
    println(json.encode(profile))
}

json.encode accepts the profile directly and produces JSON text. Choose the fields of the value you serialize deliberately; it is often useful to define a small response class instead of serializing an entire internal object.

Transform and convert#

Use json.from(profile) when you need a json.Value to inspect or modify before encoding. A json.Value can be changed, for example with document.set_bool("active", true). Converting an existing value to a class uses document.to_type[Profile](), with the same field requirements as typed decoding.

Use Files & streams to read JSON from disk, or HTTP & networking to receive it from another service. Handle transport errors separately from JSON syntax and schema errors so the caller gets a useful explanation.