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_NULL … JSON_OBJECT |
pub const i32 |
Tags de tipo, 0–6. |
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 key → v 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;
}
Navegação
| 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 viranull, entrada não parseável viraJSON_NULL). Validekind(ou use os acessoresget_*, que verificam o tipo) antes de confiar nos dados parseados.- Escapes
\uXXXXdecodificam para um único?— decodificação completa de UTF-16 / surrogate ainda não está implementada. get_intrejeita floats JSON (3.0) como tipo errado; useget_floatquando um número puder legitimamente ter parte fracionária.get_floataceita ints.JsonValue::float(n)sempre emite com parte fracionária (3.000000), nunca como inteiro simples. UseJsonValue::intpara valores inteiros na transmissão.obj_setacrescenta chaves duplicadas em vez de sobrescrever;getretorna 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 aget_*/opt_*quando isso importar.