Parsing a JSON payload without a top-level keyword

I’m calling a 3rd party http endpoint that returns a json payload like this:

[{"name": "thing1"}, {"name": "thing2"}]

It’s obviously way more complicated than that, but you get the idea. The top level object is a list, without a corresponding keyword. Whether or not that’s “proper” JSON, it’s not my service. I’m just trying to use it.

Anyway, I don’t know how to create a struct to pass to parseFromSlice, because it doesn’t have a top-level key to put into the struct.

I tried:

const Thing = struct {
    name: []u8,
};

const Things = []Thing;

but that gives me:

error: UnexpectedToken
/home/me/.local/lib/std/json/static.zig:334:53: 0x17538a5 in innerParse__anon_40140 (app)
            if (.object_begin != try source.next()) return error.UnexpectedToken;

…which isn’t really surprising, but I’m not sure how to get around it.

Does it parse to an ArrayList(Thing)?

A top-level array is valid JSON, and if I remember correctly, Zig’s json package does parse it, though I was not specifically using parseFromSlice, which may have some additional restrictions.

Unexpected token sounds like the json is not valid. []T is the correct way to parse arrays with the static api. You can see various examples here https://github.com/ziglang/zig/blob/master/lib/std/json/static_test.zig

A list on its own is valid JSON, and using a slice as the output type is correct, you don’t need to wrap this in a struct.

UnexpectedToken usually means that the type of the field doesn’t match what it found in the JSON. For example, a string instead of a boolean value (eg, “true”), or null for a field that’s not defined as an optional

1 Like

Could it be you need name: []const u8 here?

1 Like

Thanks for all the replies.

This, in particular, led me down a path to troubleshooting my struct vs the actual JSON and seeing that I was truncating the payload, so it never actually was valid.

I’ve adjusted and now it works. Thanks!

Zig’s JSON parser doesn’t parse to an ArrayList(T), but you can write a wrapper that does by implementing jsonParse. See my reply on a related post.

[]u8 is perfectly acceptable here. []const u8 would mean the array data is immutable (i.e. name is immutable), but since parseFromSlice and it’s sibling parseFromSliceLeaky allocate memory for the parsed result there aren’t any concerns about the constness of the data (there may be in your application, but from an API level there aren’t).


Anyways, I’m not sure why you’re getting an error in the first place. I suspect it’s some other issue related to your input or type definition, as the contrived example you provided works on my machine (tested with Zig versions 0.12.0, 0.13.0, 0.14.0, and 0.15.0-dev.621+a63f7875f):

const std = @import("std");

const Thing = struct {
    name: []u8,
};

const Things = []Thing;

pub fn main() !void {
    const str =
        \\[{"name": "thing1"}, {"name": "thing2"}]
    ;

    const things = try std.json.parseFromSlice(Things, std.heap.page_allocator, str, .{});

    for (things.value) |thing| {
        std.debug.print("{s}\n", .{thing.name});
    }
}

This prints

thing1
thing2

to my console.