#JSON noob who `Unable to parse into type 'void'`

1 messages · Page 1 of 1 (latest)

charred raven
#

I'm playing with std.json for the first time and didn't get how to parse these cases:

// case 1:
{}

// case 2:
{ "object": {}}

I tried this to handle the first case:

const std = @import("std");

pub fn main() !void {
    const parsed = try std.json.parseFromSlice(void, std.heap.c_allocator,
        \\{}
    , .{});
    std.log.debug("{any}", .{parsed.value});
}

But it gives:

zig run tmp.zig
/Users/timfayz/.zig/lib/std/json/static.zig:506:17: error: Unable to parse into type 'void'
        else => @compileError("Unable to parse into type '" ++ @typeName(T) ++ "'"),
                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
referenced by:
    parseFromTokenSourceLeaky__anon_6472: /Users/timfayz/.zig/lib/std/json/static.zig:140:33
    parseFromTokenSource__anon_3419: /Users/timfayz/.zig/lib/std/json/static.zig:107:49
    remaining reference traces hidden; use '-freference-trace' to see all reference traces
#

Using @TypeOf(void) instead, throws:

/Users/timfayz/.zig/lib/std/heap.zig:64:33: error: comptime call of extern function
            if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0)
                ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/timfayz/.zig/lib/std/heap.zig:109:28: note: called from here
        return alignedAlloc(len, log2_align);
               ~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
/Users/timfayz/.zig/lib/std/mem/Allocator.zig:86:29: note: called from here
    return self.vtable.alloc(self.ptr, len, ptr_align, ret_addr);
           ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/timfayz/.zig/lib/std/mem/Allocator.zig:225:35: note: called from here
    const byte_ptr = self.rawAlloc(byte_count, log2a(alignment), return_address) orelse return Error.OutOfMemory;
                     ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/timfayz/.zig/lib/std/mem/Allocator.zig:105:62: note: called from here
    const ptr: *T = @ptrCast(try self.allocBytesWithAlignment(@alignOf(T), @sizeOf(T), @returnAddress()));
                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/timfayz/.zig/lib/std/json/static.zig:100:38: note: called from here
        .arena = try allocator.create(ArenaAllocator),
                     ~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~
/Users/timfayz/.zig/lib/std/json/static.zig:73:32: note: called from here
    return parseFromTokenSource(T, allocator, &scanner, options);
           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/tmp.zig:4:47: note: called from here
    const parsed = try std.json.parseFromSlice(@TypeOf(void), std.heap.c_allocator,
                       ~~~~~~~~~~~~~~~~~~~~~~~^
referenced by:
    callMain: /Users/timfayz/.zig/lib/std/start.zig:511:32
    callMainWithArgs: /Users/timfayz/.zig/lib/std/start.zig:469:12
    remaining reference traces hidden; use '-freference-trace' to see all reference traces
#

Since I can't handle the first, no other cases that include {} in them are working.

#

I have no idea what to do...

#

Also, what I didn't understand how to see where the parsing failed. Consider the following:

const std = @import("std");

const Type = []const []const u8;

pub fn main() !void {
    const parsed = try std.json.parseFromSlice(Type, std.heap.c_allocator,
        \\["a", "b", {"this": "breaks parsing"}]
    , .{});
    std.log.debug("{any}", .{parsed.value});
}

Parsing fails saying error: UnexpectedToken. Yes but where?

error: UnexpectedToken
/Users/timfayz/.zig/lib/std/json/static.zig:500:33: 0x102f320db in innerParse__anon_7772 (tmp)
                        else => return error.UnexpectedToken,
                                ^
/Users/timfayz/.zig/lib/std/json/static.zig:467:64: 0x102f1b52f in innerParse__anon_7386 (tmp)
                                arraylist.appendAssumeCapacity(try innerParse(ptrInfo.child, allocator, source, options));
                                                               ^
/Users/timfayz/.zig/lib/std/json/static.zig:140:19: 0x102f0615b in parseFromTokenSourceLeaky__anon_6473 (tmp)
    const value = try innerParse(T, allocator, scanner_or_reader, resolved_options);
                  ^
/Users/timfayz/.zig/lib/std/json/static.zig:107:20: 0x102ede3af in parseFromTokenSource__anon_3420 (tmp)
    parsed.value = try parseFromTokenSourceLeaky(T, parsed.arena.allocator(), scanner_or_reader, options);
                   ^
/Users/timfayz/.zig/lib/std/json/static.zig:73:5: 0x102edc93b in parseFromSlice__anon_2200 (tmp)
    return parseFromTokenSource(T, allocator, &scanner, options);
#

JSON noob who Unable to parse into type 'void'

charred raven
#

It seems the only way to parse json {} object with a predefined zig type it is to use an empty struct {}:

const std = @import("std");

pub fn main() !void {
    const parsed = try std.json.parseFromSlice(struct {}, std.heap.c_allocator,
        \\{}
    , .{});
    std.log.debug("{any}", .{parsed.value});
}

Output:

tmp.main__struct_2075{ }
limpid shore
#

sounds about right

#

case 2 looks like struct { object: struct {} }

charred raven
#

Yes.

#

Any idea how to see the place where something is wrong in JSON (eg. a missing field)?

limpid shore
#

I don't believe this api is designed with that use case in mind

#

you can definitely do it yourself with the token stream api

#

obviously it's not as ergonomic

#

line 195

#

you initialize a scanner yourself, use the enableDiagnostics function, and then use parseFromTokenSource

#
const std = @import("std");

const Type = []const []const u8;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const ally = gpa.allocator();

    var scanner = std.json.Scanner.initCompleteInput(
        ally,
        \\["a", "b", {"this": "breaks parsing"}]
    );
    defer scanner.deinit();

    var diagnostics = std.json.Diagnostics{};
    scanner.enableDiagnostics(&diagnostics);

    const parsed = std.json.parseFromTokenSource(Type, ally, &scanner, .{}) catch {
        std.log.debug("parsing failed at {d}:{d}\n", .{diagnostics.getLine(), diagnostics.getColumn()});
        std.process.exit(1);
    };
    defer parsed.deinit();

    std.log.debug("{any}", .{parsed.value});
}
untold moat
charred raven
#

Right but I was interested specifically in load-into-zig-type approach :)

charred raven
#

Issue is solved now. Thank you all guys!

charred raven
#

Wait for a second. But what if, in case of using std.json.Value, at some point (say, a node .array), I realized that the structure user provided must be different. How can I point to the source where the divergence happened, ie. we expected Value.string instead of Value.array?

limpid shore
#

if you've successfully parsed a std.json.Value the scanner/reader backing your json parsing would already be fully consumed, so finding diagnostics info like the example above wouldn't be possible and Values don't store this info. some potential solutions I might use myself:

  1. assuming you're using recursive descent on the value, if you encounter a discrepency you can collect info when unwinding the recursive call. e.g. if you're in root -> array[2] -> key "hello", you can collect the index and key as you unwind
  2. switch from using std.json.Value to writing a simple recursive descent parser with std.json.TokenStream. this would allow you to use the library's diagnostic stuff and you just have to write a rudimentary parser
#

there is also always the option of using a c library for json parsing (of which there are many) if these solutions don't fit your use case

charred raven
#

Got it. @limpid shore But in case of you can collect the index and key as you unwind you meant that I can provide just the "path" information where it happened rather than precise location, right? Because knowing that info, I don't really know how I can locate the discrepancy in the actual source.