What struct to use to get required JSON string

I’m trying to learn zig by creating a little application. The application has to send HTTP POST requests to an LMS (Lyrion Music Server). Just some basic commands like play, pause, get currently played track, etc.

According to the documentation the JSON string should look like:

{"id": 1, "method": "slim.request", "params": [ 
<playerid>, [<command>, <start>, 
<itemsPerResponse>, <p3>, ... <pN> ]]}

An example of this could be

{"id":1,"method":"slim.request","params":["00:04:20:ab:cd:ef",["playlist","name","?"]]}

My initial idea was to create a struct and use ‘std.json.stringify’ to get the string that can be sent to the server. But I’m having trouble creating this struct definition. The first two fields, id and method, are easy but how do I define the ‘params’ part. It looks like an array with two entries, the first containing the player id and the second contains another array that holds the actual commands to be sent.

Any help or pointer to documentation is appreciated.

1 Like

Interesting. It looks like stringify can accept a tuple type, so here’s a way to do it.

const std = @import("std");
pub fn main() !void {
    const writer = std.io.getStdOut().writer();

    const Params = std.meta.Tuple(&.{ []const u8, []const []const u8 });

    const params: Params = .{
        "00:04:20:ab:cd:ef",
        &.{
            "playlist",
            "name",
            "?",
        },
    };
    try std.json.stringify(params, .{}, writer);
}

["00:04:20:ab:cd:ef",["playlist","name","?"]]

https://ziglang.org/documentation/master/std/#std.meta.Tuple

4 Likes

Custom serialization approach:

const std = @import("std");

const Params = struct {
    player_id: []const u8,
    options: []const []const u8,

    pub fn jsonStringify(self: @This(), writer: anytype) !void {
        try writer.beginArray();
        player_id: {
            try writer.write(self.player_id);
            break:player_id;
        }
        options: {
            try writer.write(self.options);
            break:options;
        }
        try writer.endArray();
    }
};

pub fn main() !void {
    const p: Params = .{
        .player_id = "00:04:20:ab:cd:ef",
        .options = &.{ "playlist","name","?" },
    };

    const out = std.io.getStdOut();
    try std.json.stringify(p, .{}, out.writer());
    _ = try out.write("\n");
}

3 Likes

Thanks. I did not know that tuples could be used. And also thanks for reminding me to look at the sources of the standard library as it appears there is valuable information there.

Thanks for this option. I think the solution of using tuples seems a bit easier in my situation. However I was not aware it is this easy to create your own custom serialization. I still have a lot to learn about Zig, but am enjoying the process so far.

While I think that your use of blocks looks like a fun idea that also could be really useful for complex jsonStringify functions, I think in this case it isn’t really helpful and makes it look more complicated than it is:

pub fn jsonStringify(self: @This(), writer: anytype) !void {
    try writer.beginArray();
    try writer.write(self.player_id);
    try writer.write(self.options);
    try writer.endArray();
}
2 Likes

Yes, in this case, blocks won’t be necessary.
But they might be useful in more complex cases.

1 Like