# JSON

> Parsing into real types, and back out again.

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

const Config = struct {
    name: []const u8,
    port: u16,
    debug: bool = false, // defaults cover missing fields
};

test "parse into a struct" {
    const gpa = std.testing.allocator;
    const text =
        \\{ "name": "server", "port": 8080 }
    ;

    // The parse result owns its allocations; deinit frees them all at once.
    const parsed = try std.json.parseFromSlice(Config, gpa, text, .{});
    defer parsed.deinit();

    try expect(std.mem.eql(u8, parsed.value.name, "server"));
    try expect(parsed.value.port == 8080);
    try expect(parsed.value.debug == false);
}

test "unknown fields are rejected by default" {
    const gpa = std.testing.allocator;
    const text =
        \\{ "name": "x", "port": 1, "extra": true }
    ;
    try std.testing.expectError(
        error.UnknownField,
        std.json.parseFromSlice(Config, gpa, text, .{}),
    );

    // Opt out when the payload may carry fields you do not model.
    const lenient = try std.json.parseFromSlice(Config, gpa, text, .{
        .ignore_unknown_fields = true,
    });
    defer lenient.deinit();
    try expect(lenient.value.port == 1);
}

test "dynamic values when the shape is unknown" {
    const gpa = std.testing.allocator;
    const parsed = try std.json.parseFromSlice(
        std.json.Value,
        gpa,
        \\{ "a": [1, 2, 3] }
    ,
        .{},
    );
    defer parsed.deinit();

    const array = parsed.value.object.get("a").?.array;
    try expect(array.items.len == 3);
    try expect(array.items[0].integer == 1);
}

test "serialise" {
    const gpa = std.testing.allocator;
    var out: std.Io.Writer.Allocating = .init(gpa);
    defer out.deinit();

    try std.json.Stringify.value(
        Config{ .name = "out", .port = 99, .debug = true },
        .{},
        &out.writer,
    );
    try expect(std.mem.indexOf(u8, out.written(), "\"port\":99") != null);
}
```

*Runnable: compiled to WebAssembly and executed by CI against Zig master. (`03-standard-library.json`)*

## Parsing into a struct

`parseFromSlice(T, gpa, text, .{})` maps JSON onto your type using its field
names. The result owns every allocation it made:

```zig
const parsed = try std.json.parseFromSlice(Config, gpa, text, .{});
defer parsed.deinit();
parsed.value.port;
```

Note `parsed.value`: the payload lives behind the owner so a single `deinit`
frees the whole tree. Strings in `parsed.value` point into that arena, so they
die with it; duplicate anything you need to outlive the parse.

## Missing and unknown fields

A field with a default is optional in the input. An **unknown** field is an
error by default:

```zig
error.UnknownField
```

That default is the right one: it catches typos in config files. Opt out
deliberately when consuming a payload you do not fully model:

```zig
.{ .ignore_unknown_fields = true }
```

## When the shape is not known

Parse into `std.json.Value` for a dynamic tree, then walk `.object`, `.array`,
`.integer`, `.string`, and friends. Prefer a concrete struct where you can:
you get validation and field names checked at compile time.

## Writing

`std.json.Stringify.value(x, .{}, &writer)` serialises to any writer. Pair it
with `std.Io.Writer.Allocating` to produce a string.
