Zig Project: JSON Parser CLI
A JSON parser is a program that reads JSON text and turns it into values a program can use. This lesson parses a fixed string literal, so every run prints the same result.
Parse an object
The parser fills a struct from the named fields in the text. Each field is then printed by name.
const std = @import("std");
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
var out = std.io.getStdOut().writer();
const Config = struct { name: []const u8, version: u32 };
const parsed = try std.json.parseFromSlice(Config, alloc, "{\"name\":\"parser\",\"version\":1}", .{});
defer parsed.deinit();
try out.print("name={s}\n", .{parsed.value.name});
try out.print("version={d}\n", .{parsed.value.version});
}
Parse an array
An array keeps its values in order. The program prints each value in that order, then the count.
const std = @import("std");
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
var out = std.io.getStdOut().writer();
const Tags = struct { tags: []const []const u8 };
const parsed = try std.json.parseFromSlice(Tags, alloc, "{\"tags\":[\"fast\",\"safe\",\"small\"]}", .{});
defer parsed.deinit();
for (parsed.value.tags) |tag| {
try out.print("{s}\n", .{tag});
}
try out.print("count={d}\n", .{parsed.value.tags.len});
}
Handle a parse error
A number where a string is expected does not match the struct. The program reports a parse error instead of a value.
const std = @import("std");
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
var out = std.io.getStdOut().writer();
const Bad = struct { name: []const u8 };
if (std.json.parseFromSlice(Bad, alloc, "{\"name\":42}", .{})) |parsed| {
defer parsed.deinit();
try out.print("ok\n", .{});
} else |_| {
try out.print("parse error\n", .{});
}
}
Example
const std = @import("std");
pub fn main() !void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
const alloc = arena.allocator();
var out = std.io.getStdOut().writer();
const Tool = struct { name: []const u8, retries: u32, enabled: bool, tags: []const []const u8 };
const parsed = try std.json.parseFromSlice(Tool, alloc, "{\"name\":\"tool\",\"retries\":3,\"enabled\":true,\"tags\":[\"fast\",\"safe\"]}", .{});
defer parsed.deinit();
try out.print("name={s}\n", .{parsed.value.name});
try out.print("retries={d}\n", .{parsed.value.retries});
try out.print("enabled={}\n", .{parsed.value.enabled});
try out.print("tag0={s}\n", .{parsed.value.tags[0]});
try out.print("tag1={s}\n", .{parsed.value.tags[1]});
}
Key points
- JSON: JSON is a text format for data made of objects, arrays, strings, numbers, and true or false values.
- Object: an object is a set of named fields inside braces.
- Field: a field is one name paired with one value inside an object.
- Array: an array is an ordered list of values inside brackets.
- Parsing: parsing is the act of reading text and building values from it.
- Parse error: a parse error is a failure reported when text does not match the expected shape.
The program parses one fixed object with a name, a retry count, a flag, and two tags. It prints the scalar fields first. It then prints each tag by its position in the array.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
What does parsing produce for a field written as true in the text?