How to parse JSON object with key containing dash

const std = @import("std");

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    const Response = struct {
        headers: struct {
            Host: []const u8,
            // error: MissingField
            // User_Agent: []const u8,
        },
    };
    const input =
        \\ {
        \\ "headers": {
        \\   "Accept": "application/vnd.github.v3+json",
        \\   "Host": "httpbin.org",
        \\   "User-Agent": "my http client",
        \\   "X-Amzn-Trace-Id": "Root=1-632f00bb-04724a8831e8b65c47175bba"
        \\ } }
    ;
    var stream = std.json.TokenStream.init(input);
    const resp = try std.json.parse(Response, &stream, .{
        .allocator = allocator,
        .ignore_unknown_fields = true,
    });
    std.log.info("{}", .{resp});
}

In this demo, is there any way to get User-Agent out?

1 Like

Sure there is! In Zig you can declare identifiers that contain character sequences that would otherwise be invalid if you wrap the names in @"...": Documentation - The Zig Programming Language

This works for struct field names too, and you can access those fields using the same @"..." syntax, or the @field() builtin:

$ cat field.zig 
const std = @import("std");

const FancyName = struct {
    @"field$name": []const u8,
};

pub fn main() void {
    const fns: FancyName = .{ .@"field$name" = "test" };
    // both of these do the same thing
    std.debug.print("using @\"...\": {s}\n", .{fns.@"field$name"});
    std.debug.print("using @field(): {s}\n", .{@field(fns, "field$name")});
}

$ zig build-exe field.zig && ./field
using @"...": test
using @field(): test

So, for your example above, you can use this code:

$ cat json.zig 
const std = @import("std");

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    const Response = struct {
        headers: struct {
            Host: []const u8,
            @"User-Agent": []const u8,
        },
    };
    const input =
        \\ {
        \\ "headers": {
        \\   "Accept": "application/vnd.github.v3+json",
        \\   "Host": "httpbin.org",
        \\   "User-Agent": "my http client",
        \\   "X-Amzn-Trace-Id": "Root=1-632f00bb-04724a8831e8b65c47175bba"
        \\ } }
    ;
    var stream = std.json.TokenStream.init(input);
    const resp = try std.json.parse(Response, &stream, .{
        .allocator = allocator,
        .ignore_unknown_fields = true,
    });
    std.log.info("Host: {s}", .{resp.headers.Host});
    std.log.info("User-Agent: {s}", .{resp.headers.@"User-Agent"});
}

$ zig build-exe json.zig && ./json
info: Host: httpbin.org
info: User-Agent: my http client
3 Likes