#JSON formatting changes representations based on array data

1 messages · Page 1 of 1 (latest)

rugged sun
#

When I try to JSON-encode an array of u8, I get the expected array of json ints if the data has a lot of bits set (this is the only pattern I've been able to detect) and it get a completely different string-style encoding if most or all of the bits are zero.

Here's a test that illustrates the problem. The first expectation passes, but the second fails.

test "json encoding UUIDs" {
    const alloc = std.testing.allocator;
    var allocWriter = std.io.Writer.Allocating.init(alloc);
    defer allocWriter.deinit();
    var zeroID: [16]u8 = undefined;

    @memset(zeroID[0..], 0xff);
    try std.json.fmt(zeroID, .{}).format(&allocWriter.writer);
    var written = allocWriter.written();
    try std.testing.expectEqualStrings("[255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255]", written);
    allocWriter.clearRetainingCapacity();

    @memset(zeroID[0..], 0);
    try std.json.fmt(zeroID, .{}).format(&allocWriter.writer);
    written = allocWriter.written();
    try std.testing.expectEqualStrings("[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]", written);
}

The failure looks like this:

====== expected this output: =========
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]␃

======== instead found this: =========
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"␃

======================================
First difference occurs on line 1:
expected:
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
^ ('\x5b')
found:
"\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000"
^ ('\x22')
/usr/lib/zig/std/testing.zig:673:9: 0x121c975 in expectEqualStrings (std.zig)
        return error.TestExpectedEqual;
        ^
/home/chris/Code/tapestry/tapestry/src/protocol.zig:28:5: 0x12dfb73 in test.json encoding UUIDs (tapestry.zig)
    try std.testing.expectEqualStrings("[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]", written);
    ^

Does anyone know why this might be happening? I'm wondering if this is a stdlib bug.

#

Aha! I think maybe I'm tripping over this check in Stringify.write():

                    if (!self.options.emit_strings_as_arrays and std.unicode.utf8ValidateSlice(slice)) {
                        return self.stringValue(slice);
                    }

If the data is valid UTF-8, I get a string. Otherwise the data is written as an array. My "many bits set" has a tendency to create invalid UTF-8 data, which forces the codepath that I expected. I can set emit_strings_as_arrays to force the behavior that I want, but at the cost of not being able to emit string data at all.