std.json Parsing — Desserialização de JSON
O sistema de parsing JSON do Zig permite converter strings JSON em valores Zig de forma segura e eficiente. Ele suporta tanto parsing tipado (diretamente para structs) quanto parsing dinâmico (para json.Value), além de um parser de streaming para processamento incremental de documentos grandes.
Visão Geral
const std = @import("std");
const json = std.json;
Abordagens de Parsing
parseFromSlice(T, ...)— Converte JSON diretamente em um tipo ZigTparseFromSlice(json.Value, ...)— Retorna uma árvore dinâmica navegávelScanner/Reader— Parsing token a token para streaming
Funções Principais
// Parse tipado ou dinâmico a partir de um slice
pub fn parseFromSlice(
comptime T: type,
allocator: Allocator,
source: []const u8,
options: ParseOptions,
) !Parsed(T)
// Parse a partir de um Reader (streaming)
pub fn parseFromTokenSource(
comptime T: type,
allocator: Allocator,
source: anytype,
options: ParseOptions,
) !Parsed(T)
// Scanner de baixo nível
pub fn Scanner = struct {
pub fn next(self: *Scanner) !Token
// ...
};
ParseOptions
pub const ParseOptions = struct {
// Ignora campos JSON que não existem no tipo T
ignore_unknown_fields: bool = true,
// Controle de alocação
allocate: enum { alloc_always, alloc_if_needed } = .alloc_if_needed,
// Tamanho máximo de aninhamento
max_nesting_depth: ?usize = null,
};
Exemplo 1: Parsing de API REST
const std = @import("std");
const ApiResponse = struct {
success: bool,
data: ?Data = null,
error_msg: ?[]const u8 = null,
const Data = struct {
usuarios: []const Usuario,
pagina: u32,
total_paginas: u32,
};
const Usuario = struct {
id: u64,
nome: []const u8,
email: []const u8,
admin: bool = false,
};
};
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const json_str =
\\{
\\ "success": true,
\\ "data": {
\\ "usuarios": [
\\ {"id": 1, "nome": "Ana", "email": "ana@ex.com", "admin": true},
\\ {"id": 2, "nome": "Bruno", "email": "bruno@ex.com"},
\\ {"id": 3, "nome": "Carla", "email": "carla@ex.com"}
\\ ],
\\ "pagina": 1,
\\ "total_paginas": 5
\\ }
\\}
;
const parsed = try std.json.parseFromSlice(ApiResponse, allocator, json_str, .{});
defer parsed.deinit();
const resp = parsed.value;
const stdout = std.io.getStdOut().writer();
try stdout.print("Sucesso: {}\n", .{resp.success});
if (resp.data) |data| {
try stdout.print("Página {d}/{d}\n", .{ data.pagina, data.total_paginas });
try stdout.writeAll("\nUsuários:\n");
for (data.usuarios) |user| {
const tag = if (user.admin) " [ADMIN]" else "";
try stdout.print(" {d}. {s} ({s}){s}\n", .{
user.id, user.nome, user.email, tag,
});
}
}
}
Exemplo 2: Parsing Dinâmico com Navegação
const std = @import("std");
fn acessarCampo(value: std.json.Value, caminho: []const []const u8) ?std.json.Value {
var atual = value;
for (caminho) |chave| {
switch (atual) {
.object => |obj| {
if (obj.get(chave)) |proximo| {
atual = proximo;
} else {
return null;
}
},
else => return null,
}
}
return atual;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const json_str =
\\{
\\ "app": {
\\ "nome": "MeuApp",
\\ "versao": "2.1.0",
\\ "config": {
\\ "debug": false,
\\ "max_usuarios": 1000,
\\ "features": ["auth", "cache", "logs"]
\\ }
\\ }
\\}
;
const parsed = try std.json.parseFromSlice(std.json.Value, allocator, json_str, .{});
defer parsed.deinit();
const root = parsed.value;
const stdout = std.io.getStdOut().writer();
// Acesso profundo
const caminho_nome = [_][]const u8{ "app", "nome" };
if (acessarCampo(root, &caminho_nome)) |nome| {
try stdout.print("App: {s}\n", .{nome.string});
}
const caminho_max = [_][]const u8{ "app", "config", "max_usuarios" };
if (acessarCampo(root, &caminho_max)) |max| {
try stdout.print("Max usuários: {d}\n", .{max.integer});
}
// Lista de features
const caminho_features = [_][]const u8{ "app", "config", "features" };
if (acessarCampo(root, &caminho_features)) |features| {
try stdout.writeAll("Features: ");
for (features.array.items, 0..) |item, i| {
if (i > 0) try stdout.writeAll(", ");
try stdout.writeAll(item.string);
}
try stdout.writeAll("\n");
}
}
Exemplo 3: Tratamento de Erros e Validação
const std = @import("std");
const Produto = struct {
nome: []const u8,
preco: f64,
quantidade: u32,
categoria: []const u8 = "geral",
};
fn parseProdutos(json_str: []const u8, allocator: std.mem.Allocator) ![]const Produto {
const parsed = try std.json.parseFromSlice(
struct { produtos: []const Produto },
allocator,
json_str,
.{ .ignore_unknown_fields = true },
);
// Nota: o caller é responsável por chamar parsed.deinit()
return parsed.value.produtos;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const stdout = std.io.getStdOut().writer();
// JSON válido
const valido =
\\{"produtos": [
\\ {"nome": "Café", "preco": 12.50, "quantidade": 100},
\\ {"nome": "Chá", "preco": 8.90, "quantidade": 50, "categoria": "bebidas"}
\\]}
;
const parsed = std.json.parseFromSlice(
struct { produtos: []const Produto },
allocator,
valido,
.{ .ignore_unknown_fields = true },
);
if (parsed) |p| {
defer p.deinit();
try stdout.writeAll("Produtos carregados:\n");
for (p.value.produtos) |prod| {
try stdout.print(" {s}: R${d:.2} (x{d}) [{s}]\n", .{
prod.nome, prod.preco, prod.quantidade, prod.categoria,
});
}
} else |err| {
try stdout.print("Erro ao parsear JSON: {}\n", .{err});
}
// JSON inválido — tratamento de erro
const invalido = "{ invalido }";
if (std.json.parseFromSlice(std.json.Value, allocator, invalido, .{})) |_| {
try stdout.writeAll("Inesperado: parsing bem-sucedido\n");
} else |err| {
try stdout.print("Erro esperado em JSON inválido: {}\n", .{err});
}
}
Padrões Comuns
Campos Opcionais com Valores Padrão
const Config = struct {
porta: u16 = 8080, // padrão se ausente no JSON
host: []const u8 = "localhost",
debug: bool = false,
};
Ignorar Campos Desconhecidos
const parsed = try std.json.parseFromSlice(
MeuTipo,
allocator,
json_str,
.{ .ignore_unknown_fields = true },
);
Módulos Relacionados
- std.json — Visão geral do módulo JSON
- std.json Stringify — Serialização JSON
- std.fmt — Formatação de strings
- std.mem.Allocator — Interface de alocação