Capítulo 21 17 min de leitura

JSON

Um parser e emissor de JSON minimalista construído em torno de um único tipo de união discriminada, JsonValue. UTF-8 na entrada, UTF-8 na saída. Construa árvores com construtores estáticos, mute-as com obj_set / arr_push, leia-as de volta com get / at / os acessores tipados get_* / opt_*, e faça a ida e volta com JsonValue::parse / emit.

Importação

import stdlib::json::*;

Visão geral da superfície pública

Item Tipo Resumo
JSON_NULLJSON_OBJECT pub const i32 Tags de tipo, 06.
JsonValue pub struct O nó de valor da união discriminada.
JsonParser pub struct Cursor do parser (raramente construído diretamente).
JsonBind pub trait Binding bidirecional struct ⇄ JSON.
null/bool/int/float/string/array/object construtores estáticos Alocam um novo *JsonValue no heap.
parse/emit/escape parsear/serializar Documento ⇄ string.
arr_push/obj_set mutadores Constroem arrays/objetos no lugar.
get/at/len navegação Acesso a filhos sem tipo.
as_string/as_int/as_float/as_bool/is_null acessores escalares Leituras com coerção, nunca falham.
get_string/get_int/get_bool/get_float/get_object/get_array tipados (-> !T) Leituras obrigatórias com verificação de tipo.
opt_string/opt_int/opt_bool/opt_float tipados (-> ?T) Leituras opcionais com verificação de tipo.
get_any tipado Passagem direta de qualquer tipo.

Constantes de tipo

Todo JsonValue carrega uma tag kind: i32 extraída destes valores pub const.

Constante Valor Tipo JSON
JSON_NULL 0 null
JSON_BOOL 1 true / false
JSON_INT 2 número inteiro
JSON_FLOAT 3 número de ponto flutuante
JSON_STRING 4 string
JSON_ARRAY 5 array
JSON_OBJECT 6 objeto
let v: *JsonValue = JsonValue::parse("[1,2,3]");
if v.kind == JSON_ARRAY { println!("array of", v.len()); }

A struct JsonValue

pub struct JsonValue {
    pub kind: i32,
    pub b: bool,
    pub i: i32,
    pub f: f64,
    pub s: string,
    pub arr: *Vector<JsonValue>,
    pub obj_keys: *Vector<string>,
    pub obj_vals: *Vector<JsonValue>,
}

Apenas o campo correspondente ao kind tem significado: b para JSON_BOOL, i para JSON_INT, f para JSON_FLOAT, s para JSON_STRING, arr para JSON_ARRAY, e os vetores paralelos obj_keys / obj_vals para JSON_OBJECT. Prefira os métodos acessores abaixo a acessar os campos diretamente; os acessores tipados impõem o kind correto.

Construtores (estáticos)

Cada construtor aloca um novo *JsonValue no heap.

Função Assinatura Descrição
null fn null() -> *JsonValue JSON null.
bool fn bool(b: bool) -> *JsonValue Booleano JSON.
int fn int(n: i32) -> *JsonValue Inteiro JSON (permanece inteiro até ser parseado de volta como float).
float fn float(f: f64) -> *JsonValue Float JSON; sempre emite como float, mesmo para valores inteiros.
string fn string(s: string) -> *JsonValue String JSON; escapada no momento da emissão, então passe texto bruto.
array fn array() -> *JsonValue Array vazio; preencha com arr_push.
object fn object() -> *JsonValue Objeto vazio; preencha com obj_set.
println!(JsonValue::null().emit());          // null
println!(JsonValue::bool(true).emit());      // true
println!(JsonValue::int(42).emit());         // 42
println!(JsonValue::float(3.14).emit());     // 3.140000
println!(JsonValue::string("hi").emit());    // "hi"

Parseamento e serialização

parse

pub fn parse(src: string) -> *JsonValue

Parseia um documento JSON em uma árvore. O parser é leniente: entrada malformada pode produzir uma árvore parcialmente preenchida em vez de um erro (não há resultado !T). Valide o kind resultante se a entrada for não confiável.

let v: *JsonValue = JsonValue::parse("{\"name\":\"alice\",\"age\":30}");
println!(v.get("name").as_string());   // alice
println!(v.get("age").as_int());       // 30

Recuperação leniente na prática: objetos truncados mantêm as chaves parseadas até aquele ponto (um valor sem token subsequente vira null), e entradas que não são JSON produzem um nó JSON_NULL.

import stdlib::json::*;

fn main() -> i32 {
    let v: *JsonValue = JsonValue::parse("{\"a\":1,\"b\":");
    println!(v.kind == JSON_OBJECT);   // true
    println!(v.len());                 // 2  (b -> null)

    let bad: *JsonValue = JsonValue::parse("not json");
    println!(bad.kind == JSON_NULL);   // true
    return 0;
}

emit

pub fn emit(self: *JsonValue) -> string

Serializa para uma string compacta (sem espaços em branco). O resultado pertence ao chamador no heap. Floats são renderizados com a formatação padrão de f64 do format! (ex.: 3.140000). Um ponteiro null emite como "null".

let o: *JsonValue = JsonValue::object();
o.obj_set("x", JsonValue::int(1));
o.obj_set("y", JsonValue::array());
println!(o.emit());   // {"x":1,"y":[]}

escape

pub fn escape(s: string) -> string

Envolve s em aspas duplas e escapa bytes especiais do JSON — ", \, CR, LF, tab e outros bytes de controle (< 32, renderizados como ?). É a mesma rotina que emit usa para valores string e chaves de objeto.

println!(JsonValue::escape("hi"));     // "hi"
println!(JsonValue::escape("a\nb"));   // "a\nb"

Construção

arr_push

pub fn arr_push(self: *JsonValue, v: *JsonValue)

Acrescenta v a um array JSON. Sem efeito quando self não é um array.

obj_set

pub fn obj_set(self: *JsonValue, key: string, v: *JsonValue)

Define keyv em um objeto JSON. Sem efeito quando self não é um objeto.

let user: *JsonValue = JsonValue::object();
user.obj_set("name", JsonValue::string("alice"));
user.obj_set("age",  JsonValue::int(30));
println!(user.emit());   // {"name":"alice","age":30}

let a: *JsonValue = JsonValue::array();
a.arr_push(JsonValue::int(1));
a.arr_push(JsonValue::int(2));
println!(a.emit());      // [1,2]

Aninhamento apenas armazena nós *JsonValue filhos como valores — não há API especial para arrays/objetos aninhados; você usa obj_set / arr_push passando um filho recém-construído:

import stdlib::json::*;

fn main() -> i32 {
    let root: *JsonValue = JsonValue::object();
    root.obj_set("ok", JsonValue::bool(true));

    let nums: *JsonValue = JsonValue::array();
    nums.arr_push(JsonValue::int(1));
    nums.arr_push(JsonValue::int(2));
    nums.arr_push(JsonValue::float(2.5));
    root.obj_set("nums", nums);

    let addr: *JsonValue = JsonValue::object();
    addr.obj_set("city", JsonValue::string("Rio"));
    root.obj_set("addr", addr);

    root.obj_set("note", JsonValue::null());

    println!(root.emit());
    // {"ok":true,"nums":[1,2,2.500000],"addr":{"city":"Rio"},"note":null}
    return 0;
}
Método Assinatura Retorna
get fn get(self: *JsonValue, key: string) -> *JsonValue O valor para key, ou null se ausente / não for um objeto.
at fn at(self: *JsonValue, idx: i32) -> *JsonValue O elemento no índice idx, ou null se fora do intervalo / não for um array.
len fn len(self: *JsonValue) -> i32 Contagem de elementos do array, contagem de chaves do objeto, ou 0 para escalares / null.

get e at retornam uma cópia nova do valor filho alocada no heap, portanto é seguro usá-la após o pai sair de escopo. Ambos retornam um ponteiro null do Glide em caso de falha — compare diretamente com null.

let v: *JsonValue = JsonValue::parse("{\"name\":\"alice\"}");
println!(v.get("name").as_string());   // alice
if v.get("nope") == null { println!("missing"); }

let arr: *JsonValue = JsonValue::parse("[10, 20, 30]");
println!(arr.at(1).as_int());          // 20
if arr.at(99) == null { println!("out of range"); }
println!(arr.len());                   // 3

Acessores escalares

Esses nunca falham — coercem ou retornam um valor zero quando o kind não corresponde. Use-os quando você já sabe (ou não se importa) com o tipo.

Método Assinatura Fallback
as_string fn as_string(self: *JsonValue) -> string "" para não-strings / null.
as_int fn as_int(self: *JsonValue) -> i32 Floats são truncados em direção a zero; não-números → 0.
as_float fn as_float(self: *JsonValue) -> f64 Ints são promovidos para f64; não-números → 0.0.
as_bool fn as_bool(self: *JsonValue) -> bool false para não-bools / null (sem coerção truthy).
is_null fn is_null(self: *JsonValue) -> bool true para JSON null ou um ponteiro null do Glide.
println!(JsonValue::float(3.9).as_int());    // 3
println!(JsonValue::int(7).as_float());      // 7.0
println!(JsonValue::int(1).as_bool());       // false  (sem coerção truthy)
println!(JsonValue::null().is_null());       // true

Acessores de objeto tipados

Leem um campo nomeado com verificação de tipo. Cada um existe em uma forma obrigatória retornando um Result (!T) e, para os escalares, em uma forma opcional retornando um Option (?T).

Campos obrigatórios (-> !T)

Método Assinatura Erro quando
get_string fn get_string(self, key: string) -> !string chave ausente, valor null, ou não é string.
get_int fn get_int(self, key: string) -> !i32 chave ausente, valor null, ou não é um int JSON (um JSON 3.0 é o tipo errado).
get_bool fn get_bool(self, key: string) -> !bool chave ausente, valor null, ou não é bool.
get_float fn get_float(self, key: string) -> !f64 chave ausente ou valor null; um int JSON é promovido para f64.
get_object fn get_object(self, key: string) -> !*JsonValue chave ausente, valor null, ou não é um objeto.
get_array fn get_array(self, key: string) -> !*JsonValue chave ausente, valor null, ou não é um array.

Chamar qualquer get_* em um self que não é objeto retorna err("not an object"). Chaves ausentes produzem missing key "<key>"; incompatibilidades de tipo produzem field "<key>" is not <want>. Propague com ? ou inspecione .ok / .err / .val.

import stdlib::json::*;

fn main() -> !i32 {
    let v: *JsonValue =
        JsonValue::parse("{\"name\":\"alice\",\"age\":30,\"active\":true,\"score\":3.14}");

    let name: string = v.get_string("name")?;
    let age:  i32    = v.get_int("age")?;
    let on:   bool   = v.get_bool("active")?;
    let sc:   f64    = v.get_float("score")?;
    println!(name, age, on, sc);

    let r: !i32 = v.get_int("missing");
    if !r.ok { println!(r.err); }   // missing key "missing"
    return ok(0);
}

get_object / get_array retornam o *JsonValue filho bruto para que você possa navegar mais fundo. Essa é a forma idiomática de ler documentos aninhados:

import stdlib::json::*;

fn main() -> !i32 {
    let src: string =
        "{\"user\":{\"name\":\"alice\",\"age\":30,\"active\":true},\"tags\":[\"x\",\"y\",\"z\"]}";
    let v: *JsonValue = JsonValue::parse(src);

    let user: *JsonValue = v.get_object("user")?;
    let name: string = user.get_string("name")?;
    let age:  i32    = user.get_int("age")?;
    let on:   bool   = user.get_bool("active")?;
    println!(name, age, on);   // alice 30 true

    let tags: *JsonValue = v.get_array("tags")?;
    for let i: i32 = 0; i < tags.len(); i++ {
        println!(tags.at(i).as_string());   // x, then y, then z
    }
    return ok(0);
}

Tratamento de erros sem ? — inspecione .ok / .err e ramifique:

import stdlib::json::*;

fn main() -> i32 {
    let v: *JsonValue = JsonValue::parse("{\"age\":\"30\"}");

    let r: !i32 = v.get_int("age");        // age é uma string
    if !r.ok { println!(r.err); }          // field "age" is not an int

    let m: !string = v.get_string("name"); // ausente
    if !m.ok { println!(m.err); }          // missing key "name"

    let scalar: *JsonValue = JsonValue::int(5);
    let bad: !i32 = scalar.get_int("x");   // self não é um objeto
    if !bad.ok { println!(bad.err); }      // not an object
    return 0;
}
import stdlib::json::*;

fn main() -> i32 {
    let v: *JsonValue = JsonValue::parse("{\"a\":3,\"b\":3.0}");

    let bi: !i32 = v.get_int("b");
    if !bi.ok { println!(bi.err); }    // field "b" is not an int

    let af: !f64 = v.get_float("a");
    if af.ok { println!(af.val); }     // 3.0  (int promovido)
    let bf: !f64 = v.get_float("b");
    if bf.ok { println!(bf.val); }     // 3.0
    return 0;
}

Campos opcionais (-> ?T)

Método Assinatura none() quando
opt_string fn opt_string(self, key: string) -> ?string chave ausente, JSON null, ou não é string.
opt_int fn opt_int(self, key: string) -> ?i32 chave ausente, JSON null, ou não é int JSON.
opt_bool fn opt_bool(self, key: string) -> ?bool chave ausente, JSON null, ou não é bool.
opt_float fn opt_float(self, key: string) -> ?f64 chave ausente ou não é número; um int JSON é promovido para f64.

Leia-os com match, .has / .val, ou aplique um padrão com ??.

import stdlib::json::*;

fn main() -> i32 {
    let v: *JsonValue = JsonValue::parse("{\"nick\":\"al\",\"n\":5}");

    match v.opt_string("nick") {
        some(s) => println!("nick=", s),
        none()  => println!("no nick"),
    }

    let port: i32 = v.opt_int("port") ?? 8080;   // 8080 (padrão)
    println!(port);

    let fl: ?f64 = v.opt_float("n");             // int promovido para f64
    if fl.has { println!(fl.val); }              // 5.0

    let nope: ?i32 = v.opt_int("nick");          // tipo errado -> none
    if !nope.has { println!("not an int"); }
    return 0;
}

get_any

pub fn get_any(self: *JsonValue, key: string) -> *JsonValue

Lê qualquer valor em key independentemente do tipo JSON (ou null se self não for um objeto, ou a chave estiver ausente) — útil para um campo de passagem dinâmica cuja forma você não quer restringir.

import stdlib::json::*;

fn main() -> i32 {
    let o: *JsonValue = JsonValue::parse("{\"meta\":[1,2],\"n\":5}");
    let raw: *JsonValue = o.get_any("meta");
    if raw != null { println!(raw.emit()); }   // [1,2]
    let miss: *JsonValue = o.get_any("nope");
    if miss == null { println!("absent"); }
    return 0;
}

O trait JsonBind

pub trait JsonBind {
    fn from_json(v: *JsonValue) -> !Self;
    fn to_json(self: *Self) -> *JsonValue;
}

Binding JSON bidirecional para suas próprias structs. Implementar JsonBind permite que um tipo flua pelos helpers tipados de requisição HTTP (json_respond, HttpResponse::ok().json_of(...), a proc macro @handler — veja o capítulo HTTP) sem precisar escrever a cerimônia de parse/emit manualmente a cada vez. from_json retorna !Self para que a validação dos elementos se propague com ?; to_json constrói uma árvore *JsonValue.

Uma ida e volta completa, incluindo um campo opcional tratado com opt_int / .has:

import stdlib::json::*;

struct Pet {
    pub name: string,
    pub age:  ?i32,
}

impl JsonBind for Pet {
    fn from_json(v: *JsonValue) -> !Pet {
        if v.kind != JSON_OBJECT { return err("expected object"); }
        return ok(Pet {
            name: v.get_string("name")?,
            age:  v.opt_int("age"),
        });
    }
    fn to_json(self: *Pet) -> *JsonValue {
        let o: *JsonValue = JsonValue::object();
        o.obj_set("name", JsonValue::string(self.name));
        if self.age.has { o.obj_set("age", JsonValue::int(self.age.val)); }
        return o;
    }
}

fn main() -> !i32 {
    let p: Pet = Pet::from_json(JsonValue::parse("{\"name\":\"rex\",\"age\":4}"))?;
    println!(p.name);                      // rex
    if p.age.has { println!(p.age.val); }  // 4
    println!((&p).to_json().emit());       // {"name":"rex","age":4}

    let q: Pet = Pet::from_json(JsonValue::parse("{\"name\":\"tom\"}"))?;
    println!((&q).to_json().emit());       // {"name":"tom"}  (age omitted)
    return ok(0);
}

Implementação genérica para Vector<T>

impl<T: JsonBind> JsonBind for Vector<T>

Qualquer Vector<T> cujo T implementa JsonBind também é bindável para JSON: um array JSON mapeia para um Vector<T> e vice-versa. Falhas de from_json no nível do elemento sobem via ?, portanto o primeiro elemento inválido falha o parse inteiro — não há ignoramento silencioso. Arrays vazios parseiam para vetores vazios; entrada que não é array resulta em erro com "expected array".

import stdlib::json::*;

@derive(JsonBind)
struct Pet { pub name: string, pub age: i32 }

fn main() -> i32 {
    let xs: *Vector<Pet> = Vector::new();
    xs.push(Pet { name: "rex", age: 4 });
    xs.push(Pet { name: "tom", age: 7 });

    let js: string = xs.to_json().emit();
    println!(js);   // [{"name":"rex","age":4},{"name":"tom","age":7}]

    let good: !Vector<Pet> = Vector::from_json(JsonValue::parse(js));
    if good.ok { println!(good.val.len()); }   // 2

    // um elemento inválido falha o parse inteiro
    let bad: !Vector<Pet> =
        Vector::from_json(JsonValue::parse("[{\"name\":\"a\",\"age\":1},{\"name\":\"b\",\"age\":\"x\"}]"));
    if !bad.ok { println!(bad.err); }   // field "age" is not an int

    // entrada que não é array
    let notarr: !Vector<Pet> = Vector::from_json(JsonValue::parse("{}"));
    if !notarr.ok { println!(notarr.err); }   // expected array
    return 0;
}

A struct JsonParser

pub struct JsonParser {
    pub src: string,
    pub pos: i32,
    pub n: i32,
}

O cursor que JsonValue::parse usa internamente. É público, mas raramente construído diretamente — prefira JsonValue::parse(src), que monta um JsonParser { src: src, pos: 0, n: src.len() } para você e retorna a árvore parseada.

Veja também

  • Capítulo HTTP — os helpers de resposta baseados em JsonBind (json_respond, HttpResponse::ok().json_of(...), a proc macro @handler) vivem do lado HTTP e consomem os tipos documentados aqui.
  • Capítulo Hashmap — objetos JSON usam vetores paralelos com ordem de inserção, não um hash map; recorra ao módulo de hashmap quando precisar de buscas por chave em escala.

Ressalvas

  • JsonValue::parse é leniente e nunca retorna um erro; entrada truncada ou malformada produz uma árvore com melhor esforço (um valor sem continuação vira null, entrada não parseável vira JSON_NULL). Valide kind (ou use os acessores get_*, que verificam o tipo) antes de confiar nos dados parseados.
  • Escapes \uXXXX decodificam para um único ? — decodificação completa de UTF-16 / surrogate ainda não está implementada.
  • get_int rejeita floats JSON (3.0) como tipo errado; use get_float quando um número puder legitimamente ter parte fracionária. get_float aceita ints.
  • JsonValue::float(n) sempre emite com parte fracionária (3.000000), nunca como inteiro simples. Use JsonValue::int para valores inteiros na transmissão.
  • obj_set acrescenta chaves duplicadas em vez de sobrescrever; get retorna a primeira ocorrência. Construa um objeto novo para "substituir" um valor.
  • Os acessores de coerção as_* não conseguem distinguir uma chave ausente (null) de um valor zero real; recorra a get_* / opt_* quando isso importar.