std.json em Zig — Referência e Exemplos

std.json — Parsing e Serialização JSON

O módulo std.json fornece funcionalidades completas para trabalhar com JSON no Zig. Ele inclui um parser que pode desserializar JSON diretamente em structs tipadas do Zig (usando parseFromSlice) e um sistema de serialização que converte valores Zig em JSON (usando stringify). A abordagem é fortemente tipada e verificada em tempo de compilação.

Visão Geral

const std = @import("std");
const json = std.json;

O sistema JSON do Zig oferece duas abordagens:

  1. Tipada: Desserializa diretamente em structs Zig — ideal quando a estrutura é conhecida
  2. Dinâmica: Usa json.Value para JSON de estrutura desconhecida

Tipos Principais

json.Value

Representação dinâmica de qualquer valor JSON:

pub const Value = union(enum) {
    null,
    bool: bool,
    integer: i64,
    float: f64,
    number_string: []const u8,
    string: []const u8,
    array: Array,
    object: ObjectMap,
};

Funções Principais

// Parse tipado — desserializa em struct T
pub fn parseFromSlice(
    comptime T: type,
    allocator: Allocator,
    input: []const u8,
    options: ParseOptions,
) !Parsed(T)

// Parse dinâmico — retorna json.Value
pub fn parseFromSlice(
    json.Value,
    allocator: Allocator,
    input: []const u8,
    options: ParseOptions,
) !Parsed(json.Value)

// Serialização — converte valor Zig para JSON
pub fn stringify(value: anytype, options: StringifyOptions, writer: anytype) !void

// Serialização para string alocada
pub fn stringifyAlloc(allocator: Allocator, value: anytype, options: StringifyOptions) ![]u8

Exemplo 1: Desserialização Tipada

const std = @import("std");

const Usuario = struct {
    nome: []const u8,
    idade: u32,
    email: ?[]const u8 = null,
    ativo: bool = true,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const json_str =
        \\{
        \\  "nome": "Maria Silva",
        \\  "idade": 28,
        \\  "email": "maria@exemplo.com",
        \\  "ativo": true
        \\}
    ;

    const parsed = try std.json.parseFromSlice(Usuario, allocator, json_str, .{});
    defer parsed.deinit();

    const user = parsed.value;

    const stdout = std.io.getStdOut().writer();
    try stdout.print("Nome:  {s}\n", .{user.nome});
    try stdout.print("Idade: {d}\n", .{user.idade});
    if (user.email) |email| {
        try stdout.print("Email: {s}\n", .{email});
    }
    try stdout.print("Ativo: {}\n", .{user.ativo});
}

Exemplo 2: Parse Dinâmico e Navegação

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const json_str =
        \\{
        \\  "servidor": {
        \\    "host": "localhost",
        \\    "porta": 8080,
        \\    "tls": false
        \\  },
        \\  "banco": {
        \\    "url": "postgres://localhost/app",
        \\    "pool_size": 10
        \\  },
        \\  "tags": ["web", "api", "prod"]
        \\}
    ;

    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();

    // Navega na estrutura
    if (root.object.get("servidor")) |servidor| {
        if (servidor.object.get("host")) |host| {
            try stdout.print("Host: {s}\n", .{host.string});
        }
        if (servidor.object.get("porta")) |porta| {
            try stdout.print("Porta: {d}\n", .{porta.integer});
        }
    }

    // Itera sobre array
    if (root.object.get("tags")) |tags| {
        try stdout.writeAll("Tags: ");
        for (tags.array.items, 0..) |tag, i| {
            if (i > 0) try stdout.writeAll(", ");
            try stdout.print("{s}", .{tag.string});
        }
        try stdout.writeAll("\n");
    }
}

Exemplo 3: Serialização de Structs para JSON

const std = @import("std");

const Resposta = struct {
    status: u16,
    mensagem: []const u8,
    dados: ?Dados = null,

    const Dados = struct {
        itens: []const Item,
        total: u32,
    };

    const Item = struct {
        id: u32,
        nome: []const u8,
        preco: f64,
    };
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const itens = [_]Resposta.Item{
        .{ .id = 1, .nome = "Teclado", .preco = 149.90 },
        .{ .id = 2, .nome = "Mouse", .preco = 79.50 },
        .{ .id = 3, .nome = "Monitor", .preco = 1899.00 },
    };

    const resposta = Resposta{
        .status = 200,
        .mensagem = "Sucesso",
        .dados = .{
            .itens = &itens,
            .total = 3,
        },
    };

    // Serializa com formatação
    const json_output = try std.json.stringifyAlloc(allocator, resposta, .{
        .whitespace = .indent_2,
    });
    defer allocator.free(json_output);

    const stdout = std.io.getStdOut().writer();
    try stdout.print("{s}\n", .{json_output});
}

Opções de Configuração

ParseOptions

.{
    .ignore_unknown_fields = true,  // Ignora campos não mapeados
    .allocate = .alloc_always,      // Aloca strings
}

StringifyOptions

.{
    .whitespace = .indent_2,       // JSON formatado (2 espaços)
    .whitespace = .indent_4,       // JSON formatado (4 espaços)
    .whitespace = .minified,       // JSON compacto (padrão)
    .emit_null_optional_fields = false, // Omite campos null
}

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.