---
title: "Como Fazer Streaming de JSON em Zig"
url: "https://ziglang.com.br/receitas/como-fazer-streaming-de-json-em-zig/"
markdown_url: "https://ziglang.com.br/receitas/como-fazer-streaming-de-json-em-zig.MD"
description: "Aprenda a parsear JSON em streaming com Zig usando std.json.Scanner para processar arquivos grandes de forma eficiente sem carregar tudo na memória."
date: "2026-02-21"
author: "Zig Brasil"
---

# Como Fazer Streaming de JSON em Zig

Aprenda a parsear JSON em streaming com Zig usando std.json.Scanner para processar arquivos grandes de forma eficiente sem carregar tudo na memória.


## Introdução

Quando você precisa processar arquivos JSON muito grandes (centenas de megabytes ou gigabytes), carregar tudo na memória não é viável. O streaming JSON permite processar os dados token por token, consumindo memória constante independente do tamanho do arquivo.

Em Zig, `std.json.Scanner` oferece um parser de baixo nível que emite tokens conforme lê o JSON, permitindo processamento eficiente de dados massivos.

## Pré-requisitos

- Zig instalado (versão 0.13+). Veja o [guia de instalação](/tutoriais/como-instalar-zig/)
- Familiaridade com [parsing JSON](/receitas/zig-parse-json/) em Zig

## Scanner JSON Básico

Use o scanner para processar JSON token por token:

```zig
const std = @import("std");

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

    const json_str =
        \\{"nome": "Maria", "idade": 28, "ativo": true}
    ;

    // Criar scanner
    var scanner = std.json.Scanner.initCompleteInput(allocator, json_str);
    defer scanner.deinit();

    // Processar tokens
    while (true) {
        const token = try scanner.next();
        switch (token) {
            .object_begin => std.debug.print("Início de objeto\n", .{}),
            .object_end => std.debug.print("Fim de objeto\n", .{}),
            .array_begin => std.debug.print("Início de array\n", .{}),
            .array_end => std.debug.print("Fim de array\n", .{}),
            .string => |s| std.debug.print("String: \"{s}\"\n", .{s}),
            .number => |n| std.debug.print("Número: {s}\n", .{n}),
            .true => std.debug.print("Boolean: true\n", .{}),
            .false => std.debug.print("Boolean: false\n", .{}),
            .null => std.debug.print("Null\n", .{}),
            .end_of_document => {
                std.debug.print("Fim do documento\n", .{});
                break;
            },
            else => {},
        }
    }
}
```

### Saída esperada

```
Início de objeto
String: "nome"
String: "Maria"
String: "idade"
Número: 28
String: "ativo"
Boolean: true
Fim de objeto
Fim do documento
```

## Streaming de Arquivo Grande

Processe um arquivo JSON grande sem carregar tudo na memória:

```zig
const std = @import("std");
const fs = std.fs;

const Estatisticas = struct {
    total_objetos: u64 = 0,
    total_strings: u64 = 0,
    total_numeros: u64 = 0,
    total_booleans: u64 = 0,
    total_nulls: u64 = 0,
    profundidade_max: u32 = 0,
    profundidade_atual: u32 = 0,
};

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

    // Simular dados grandes criando o arquivo
    const file = try fs.cwd().createFile("dados_grandes.json", .{});
    {
        defer file.close();
        const writer = file.writer();
        try writer.writeAll("[");
        for (0..1000) |i| {
            if (i > 0) try writer.writeAll(",");
            try writer.print(
                \\{{"id":{d},"nome":"Usuario {d}","ativo":true,"pontos":{d}}}
            , .{ i, i, i * 10 });
        }
        try writer.writeAll("]");
    }

    // Agora ler em streaming
    const data = try fs.cwd().readFileAlloc(allocator, "dados_grandes.json", 100 * 1024 * 1024);
    defer allocator.free(data);

    var scanner = std.json.Scanner.initCompleteInput(allocator, data);
    defer scanner.deinit();

    var stats = Estatisticas{};

    while (true) {
        const token = try scanner.next();
        switch (token) {
            .object_begin => {
                stats.total_objetos += 1;
                stats.profundidade_atual += 1;
                if (stats.profundidade_atual > stats.profundidade_max) {
                    stats.profundidade_max = stats.profundidade_atual;
                }
            },
            .object_end => stats.profundidade_atual -= 1,
            .array_begin => {
                stats.profundidade_atual += 1;
                if (stats.profundidade_atual > stats.profundidade_max) {
                    stats.profundidade_max = stats.profundidade_atual;
                }
            },
            .array_end => stats.profundidade_atual -= 1,
            .string => stats.total_strings += 1,
            .number => stats.total_numeros += 1,
            .true, .false => stats.total_booleans += 1,
            .null => stats.total_nulls += 1,
            .end_of_document => break,
            else => {},
        }
    }

    std.debug.print("Estatísticas do JSON:\n", .{});
    std.debug.print("  Objetos: {d}\n", .{stats.total_objetos});
    std.debug.print("  Strings: {d}\n", .{stats.total_strings});
    std.debug.print("  Números: {d}\n", .{stats.total_numeros});
    std.debug.print("  Booleans: {d}\n", .{stats.total_booleans});
    std.debug.print("  Nulls: {d}\n", .{stats.total_nulls});
    std.debug.print("  Profundidade máxima: {d}\n", .{stats.profundidade_max});
}
```

## Extrair Campos Específicos em Streaming

Procure campos específicos sem parsear o objeto inteiro:

```zig
const std = @import("std");

fn findFieldValue(
    allocator: std.mem.Allocator,
    json_str: []const u8,
    target_field: []const u8,
) !?[]const u8 {
    var scanner = std.json.Scanner.initCompleteInput(allocator, json_str);
    defer scanner.deinit();

    var depth: u32 = 0;
    var in_target_object = false;
    var found_key = false;

    while (true) {
        const token = try scanner.next();
        switch (token) {
            .object_begin => {
                depth += 1;
                if (depth == 1) in_target_object = true;
            },
            .object_end => {
                depth -= 1;
                if (depth == 0) in_target_object = false;
            },
            .string => |s| {
                if (found_key) {
                    // Este é o valor que procuramos
                    return try allocator.dupe(u8, s);
                }
                if (in_target_object and depth == 1 and std.mem.eql(u8, s, target_field)) {
                    found_key = true;
                }
            },
            .number => |n| {
                if (found_key) {
                    return try allocator.dupe(u8, n);
                }
            },
            .end_of_document => break,
            else => {
                if (found_key) found_key = false;
            },
        }
    }

    return null;
}

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

    const json =
        \\{
        \\  "id": "abc123",
        \\  "nome": "Produto Especial",
        \\  "preco": "299.90",
        \\  "categoria": "eletrônicos",
        \\  "descricao": "Um produto muito especial"
        \\}
    ;

    // Procurar apenas o campo "nome"
    if (try findFieldValue(allocator, json, "nome")) |valor| {
        defer allocator.free(valor);
        std.debug.print("Nome encontrado: {s}\n", .{valor});
    }

    if (try findFieldValue(allocator, json, "preco")) |valor| {
        defer allocator.free(valor);
        std.debug.print("Preço encontrado: {s}\n", .{valor});
    }

    if (try findFieldValue(allocator, json, "inexistente")) |_| {
        std.debug.print("Não deveria chegar aqui\n", .{});
    } else {
        std.debug.print("Campo 'inexistente' não encontrado (esperado)\n", .{});
    }
}
```

## Contar Elementos em Array Sem Carregar Tudo

Conte elementos sem alocar memória para o array inteiro:

```zig
const std = @import("std");

fn countArrayElements(allocator: std.mem.Allocator, json_str: []const u8) !u64 {
    var scanner = std.json.Scanner.initCompleteInput(allocator, json_str);
    defer scanner.deinit();

    var count: u64 = 0;
    var depth: u32 = 0;
    var in_root_array = false;

    while (true) {
        const token = try scanner.next();
        switch (token) {
            .array_begin => {
                depth += 1;
                if (depth == 1) in_root_array = true;
            },
            .array_end => {
                depth -= 1;
                if (depth == 0) in_root_array = false;
            },
            .object_begin => {
                if (in_root_array and depth == 1) count += 1;
                depth += 1;
            },
            .object_end => depth -= 1,
            .string, .number, .true, .false, .null => {
                if (in_root_array and depth == 1) count += 1;
            },
            .end_of_document => break,
            else => {},
        }
    }

    return count;
}

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

    const json = "[1, 2, 3, {\"a\": 1}, [5, 6], \"texto\", null, true]";

    const count = try countArrayElements(allocator, json);
    std.debug.print("Elementos no array: {d}\n", .{count});
}
```

### Saída esperada

```
Elementos no array: 8
```

## Dicas e Boas Práticas

1. **Use streaming para arquivos grandes**: Se o JSON tem mais de alguns MB, streaming é mais eficiente.

2. **Memória constante**: O scanner usa memória proporcional à profundidade de aninhamento, não ao tamanho do arquivo.

3. **Combine com parsing tipado**: Use streaming para encontrar seções e parsing tipado para processar sub-objetos.

4. **Trate erros de sintaxe**: O scanner retorna erros quando encontra JSON inválido.

5. **Performance**: O scanner é significativamente mais rápido para arquivos grandes pois evita alocações.

## Receitas Relacionadas

- [Como parsear JSON em Zig](/receitas/zig-parse-json/) - Parsing completo
- [Como gerar JSON em Zig](/receitas/zig-gerar-json/) - Serialização
- [Como ler/escrever JSON em arquivos](/receitas/zig-json-arquivo/) - I/O de arquivos
- [Como validar JSON em Zig](/receitas/zig-json-validar/) - Validação

## Tutoriais Relacionados

- [Parsing JSON em Zig](/tutoriais/parsing-json-zig/)
- [File I/O em Zig](/tutoriais/zig-file-io/)
