We’re going to work with the json standard library for this post. It’s got everything we need for basic JSON processing and makes it convenient. We’ll work with zig structures, convert into JSON strings, then take in JSON strings and create a structure from it to make it useable in your source code.
Serialize JSON #
Our function below takes any struct and returns a JSON object stringified to raw JSON. You must call allocator.free() on the returned object to clean up allocated resources. It let’s us take any structure we work with in our code and get a raw JSON string useable for transmitting to HTTP APIs or other communications.
pub fn Serialize(allocator: std.mem.Allocator, any: anytype) ![]u8 {
var out: std.Io.Writer.Allocating = .init(allocator);
defer out.deinit();
var write_stream: std.json.Stringify = .{
.writer = &out.writer,
.options = .{ .whitespace = .indent_2 }, // this option is for pretty printing, remove if you don't need it
};
try write_stream.write(any);
return out.toOwnedSlice();
}
Deserialize JSON #
What Deserializing JSON does is take a raw JSON string and plug the values into a struct. This function takes the raw JSON in as json_str and returns the result packaged in a std.json.Parsed. You must call deinit() on the returned object to clean up allocated resources.
pub fn Deserialize(allocator: std.mem.Allocator, comptime T: type, json_str: []const u8) !std.json.Parsed(T) {
const parsed = try std.json.parseFromSlice(T, allocator, json_str, .{ .ignore_unknown_fields = true });
return parsed;
}
Let’s use it! #
Below is code that makes use of the Deserialize() and Serialize() functions we wrote above. We first declare a string to use as raw JSON for Deserializing into a struct, then we take our struct and Serialize it back into raw JSON. The call sites are clean and easy to use.
const my_json_str =
\\{
\\ "userid": 103609,
\\ "verified": true,
\\ "access_privileges": [
\\ "user",
\\ "admin"
\\ ]
\\}
;
const User = struct {
userid: i32,
verified: bool,
access_privileges: []const []const u8,
};
pub fn main() !void {
var gpa: std.heap.DebugAllocator(.{}) = .init;
const allocator = gpa.allocator();
defer {
const deinit_status = gpa.deinit();
if (deinit_status == .leak) std.testing.expect(false) catch @panic("memory leak detected");
}
// Deserialize.
const deserialized_json = try Deserialize(allocator, User, my_json_str);
defer deserialized_json.deinit();
std.debug.print("{d}\n", .{deserialized_json.value.userid});
// Serialize.
const user: User = .{ .userid = 103609, .verified = false, .access_privileges = &.{ "user", "admin" } };
const serialized_json = try Serialize(allocator, user);
defer allocator.free(serialized_json);
std.debug.print("{s}\n", .{serialized_json});
}