Function write [src]
Renders the given Zig value as JSON.
Supported types:
Zig bool -> JSON true or false.
Zig ?T -> null or the rendering of T.
Zig i32, u64, etc. -> JSON number or string.
When option emit_nonportable_numbers_as_strings is true, if the value is outside the range +-1<<53 (the precise integer range of f64), it is rendered as a JSON string in base 10. Otherwise, it is rendered as JSON number.
Zig floats -> JSON number or string.
If the value cannot be precisely represented by an f64, it is rendered as a JSON string. Otherwise, it is rendered as JSON number.
TODO: Float rendering will likely change in the future, e.g. to remove the unnecessary "e+00".
Zig []const u8, []u8, *[N]u8, @Vector(N, u8), and similar -> JSON string.
See Options.emit_strings_as_arrays.
If the content is not valid UTF-8, rendered as an array of numbers instead.
Zig []T, [N]T, *[N]T, @Vector(N, T), and similar -> JSON array of the rendering of each item.
Zig tuple -> JSON array of the rendering of each item.
Zig struct -> JSON object with each field in declaration order.
If the struct declares a method pub fn jsonStringify(self: *@This(), jw: anytype) !void, it is called to do the serialization instead of the default behavior. The given jw is a pointer to this Stringify. See std.json.Value for an example.
See Options.emit_null_optional_fields.
Zig union(enum) -> JSON object with one field named for the active tag and a value representing the payload.
If the payload is void, then the emitted value is {}.
If the union declares a method pub fn jsonStringify(self: *@This(), jw: anytype) !void, it is called to do the serialization instead of the default behavior. The given jw is a pointer to this Stringify.
Zig enum -> JSON string naming the active tag.
If the enum declares a method pub fn jsonStringify(self: *@This(), jw: anytype) !void, it is called to do the serialization instead of the default behavior. The given jw is a pointer to this Stringify.
If the enum is non-exhaustive, unnamed values are rendered as integers.
Zig untyped enum literal -> JSON string naming the active tag.
Zig error -> JSON string naming the error.
Zig *T -> the rendering of T. Note there is no guard against circular-reference infinite recursion.
See also alternative functions print and beginWriteRaw.
For writing object field names, use objectField instead.
Prototype
pub fn write(self: *Stringify, v: anytype) Error!void Parameters
self: *Stringify Possible Errors
Source
pub fn write(self: *Stringify, v: anytype) Error!void {
if (build_mode_has_safety) assert(self.raw_streaming_mode == .none);
const T = @TypeOf(v);
switch (@typeInfo(T)) {
.int => {
try self.valueStart();
if (self.options.emit_nonportable_numbers_as_strings and
(v <= -(1 << 53) or v >= (1 << 53)))
{
try self.writer.print("\"{}\"", .{v});
} else {
try self.writer.print("{}", .{v});
}
self.valueDone();
return;
},
.comptime_int => {
return self.write(@as(std.math.IntFittingRange(v, v), v));
},
.float, .comptime_float => {
if (@as(f64, @floatCast(v)) == v) {
try self.valueStart();
try self.writer.print("{}", .{@as(f64, @floatCast(v))});
self.valueDone();
return;
}
try self.valueStart();
try self.writer.print("\"{}\"", .{v});
self.valueDone();
return;
},
.bool => {
try self.valueStart();
try self.writer.writeAll(if (v) "true" else "false");
self.valueDone();
return;
},
.null => {
try self.valueStart();
try self.writer.writeAll("null");
self.valueDone();
return;
},
.optional => {
if (v) |payload| {
return try self.write(payload);
} else {
return try self.write(null);
}
},
.@"enum" => |enum_info| {
if (std.meta.hasFn(T, "jsonStringify")) {
return v.jsonStringify(self);
}
if (!enum_info.is_exhaustive) {
inline for (enum_info.fields) |field| {
if (v == @field(T, field.name)) {
break;
}
} else {
return self.write(@intFromEnum(v));
}
}
return self.stringValue(@tagName(v));
},
.enum_literal => {
return self.stringValue(@tagName(v));
},
.@"union" => {
if (std.meta.hasFn(T, "jsonStringify")) {
return v.jsonStringify(self);
}
const info = @typeInfo(T).@"union";
if (info.tag_type) |UnionTagType| {
try self.beginObject();
inline for (info.fields) |u_field| {
if (v == @field(UnionTagType, u_field.name)) {
try self.objectField(u_field.name);
if (u_field.type == void) {
// void v is {}
try self.beginObject();
try self.endObject();
} else {
try self.write(@field(v, u_field.name));
}
break;
}
} else {
unreachable; // No active tag?
}
try self.endObject();
return;
} else {
@compileError("Unable to stringify untagged union '" ++ @typeName(T) ++ "'");
}
},
.@"struct" => |S| {
if (std.meta.hasFn(T, "jsonStringify")) {
return v.jsonStringify(self);
}
if (S.is_tuple) {
try self.beginArray();
} else {
try self.beginObject();
}
inline for (S.fields) |Field| {
// don't include void fields
if (Field.type == void) continue;
var emit_field = true;
// don't include optional fields that are null when emit_null_optional_fields is set to false
if (@typeInfo(Field.type) == .optional) {
if (self.options.emit_null_optional_fields == false) {
if (@field(v, Field.name) == null) {
emit_field = false;
}
}
}
if (emit_field) {
if (!S.is_tuple) {
try self.objectField(Field.name);
}
try self.write(@field(v, Field.name));
}
}
if (S.is_tuple) {
try self.endArray();
} else {
try self.endObject();
}
return;
},
.error_set => return self.stringValue(@errorName(v)),
.pointer => |ptr_info| switch (ptr_info.size) {
.one => switch (@typeInfo(ptr_info.child)) {
.array => {
// Coerce `*[N]T` to `[]const T`.
const Slice = []const std.meta.Elem(ptr_info.child);
return self.write(@as(Slice, v));
},
else => {
return self.write(v.*);
},
},
.many, .slice => {
if (ptr_info.size == .many and ptr_info.sentinel() == null)
@compileError("unable to stringify type '" ++ @typeName(T) ++ "' without sentinel");
const slice = if (ptr_info.size == .many) std.mem.span(v) else v;
if (ptr_info.child == u8) {
// This is a []const u8, or some similar Zig string.
if (!self.options.emit_strings_as_arrays and std.unicode.utf8ValidateSlice(slice)) {
return self.stringValue(slice);
}
}
try self.beginArray();
for (slice) |x| {
try self.write(x);
}
try self.endArray();
return;
},
else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
},
.array => {
// Coerce `[N]T` to `*const [N]T` (and then to `[]const T`).
return self.write(&v);
},
.vector => |info| {
const array: [info.len]info.child = v;
return self.write(&array);
},
else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
}
unreachable;
}