Search code examples
zig

Failed to stringify a struct error 'type' has no members


I read this but did not get it.

I've the below code that is parsing a JSON string into a struct correctl, I'm trying to do the opposite, that is stringifying a struct as a json string, but looks i'm taking a wrong approach:

const std = @import("std");

const my_json =
    \\{
    \\    "vals": {
    \\        "testing": 1,
    \\        "production": 42
    \\    },
    \\    "uptime": 9999
    \\}
;

const Config = struct {
    vals: struct {
        testing: u8,
        production: u8,
    },
    uptime: u64,
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    defer if (gpa.deinit() != .ok) {
        std.log.err("We got a leack", .{});
    } else {
        std.log.debug("memory managed correctly", .{});
    };

    const config = try std.json.parseFromSlice(Config, allocator, my_json, .{});
    defer config.deinit();

    std.log.info("{}", .{config.value.vals.testing});
    std.log.info("{}", .{config.value.vals.production});
    std.log.info("{}", .{config.value.uptime});

    const config2 = try std.json.parseFromSlice(std.json.Value, allocator, my_json, .{});
    defer config2.deinit();
    std.log.info("production: {}", .{config2.value.object.get("vals").?.object.get("production").?.integer});

    var conf3 = Config{
        .vals = .{
            .testing = 2,
            .production = 48,
        },
        .uptime = 15,
    };

    std.log.info("conf3 {any}", .{conf3});

    var string = std.ArrayList(u8).init(allocator);
    defer allocator.free(string);
    if (std.json.stringify(conf3, .{}, string.writer())) |json| {
        std.log.info("json: {any}", .{json});
    } else |err| {
        std.log.err(" {any}", .{err});
    }
}

I got the error:

/usr/lib/zig/std/mem/Allocator.zig:307:45: error: access of union field 'Pointer' while field 'Struct' is active
    const Slice = @typeInfo(@TypeOf(memory)).Pointer;
                  ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
/usr/lib/zig/std/builtin.zig:228:18: note: union declared here
pub const Type = union(enum) {
                 ^~~~~

Solution

  • I found the answer, I should be using stringifyAlloc as:

        if (std.json.stringifyAlloc(allocator, conf3, .{})) |json| {
            defer allocator.free(json);
            std.log.info("json: {s}", .{json});
        } else |err| {
            std.log.err(" {any}", .{err});
        }
    

    Now my full running code is:

    // curl -X GET "https://httpbin.org/get" -H "accept: application/json"
    // Zig version: 0.11.0
    
    const std = @import("std");
    
    const my_json =
        \\{
        \\    "vals": {
        \\        "testing": 1,
        \\        "production": 42
        \\    },
        \\    "uptime": 9999
        \\}
    ;
    
    const Config = struct {
        vals: struct {
            testing: u8,
            production: u8,
        },
        uptime: u64,
    };
    
    pub fn main() !void {
        var gpa = std.heap.GeneralPurposeAllocator(.{}){};
        const allocator = gpa.allocator();
        defer if (gpa.deinit() != .ok) {
            std.log.err("oh no, we've got a leak", .{});
        } else {
            std.log.debug("memory managed correctly", .{});
        };
    
        const config = try std.json.parseFromSlice(Config, allocator, my_json, .{});
        defer config.deinit();
    
        std.log.info("{}", .{config.value.vals.testing});
        std.log.info("{}", .{config.value.vals.production});
        std.log.info("{}", .{config.value.uptime});
    
        const config2 = try std.json.parseFromSlice(std.json.Value, allocator, my_json, .{});
        defer config2.deinit();
        std.log.info("production: {}", .{config2.value.object.get("vals").?.object.get("production").?.integer});
    
        var conf3 = Config{
            .vals = .{
                .testing = 2,
                .production = 48,
            },
            .uptime = 15,
        };
    
        std.log.info("conf3 {any}", .{conf3});
    
        if (std.json.stringifyAlloc(allocator, conf3, .{})) |json| {
            defer allocator.free(json);
            std.log.info("json: {s}", .{json});
        } else |err| {
            std.log.err(" {any}", .{err});
        }
    }
    

    And got the output correctly as:

    info: 1
    info: 42
    info: 9999
    info: production: 42
    info: conf3 json.Config{ .vals = json.Config.Config__struct_4384{ .testing = 2, .production = 48 }, .uptime = 15 }
    info: json: {"vals":{"testing":2,"production":48},"uptime":15}
    debug: memory managed correctly