write
Renders the given Zig value as JSON.
Supported types:
- Zig
bool-> JSONtrueorfalse. - Zig
?T->nullor the rendering ofT. - Zig
i32,u64, etc. -> JSON number or string.- When option
emit_nonportable_numbers_as_stringsis 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.
- When option
- 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.
- See
- 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 givenjwis a pointer to thisStringify. Seestd.json.Valuefor an example. - See
Options.emit_null_optional_fields.
- If the struct declares a method
- 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 givenjwis a pointer to thisStringify.
- If the payload is
- 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 givenjwis a pointer to thisStringify. - If the enum is non-exhaustive, unnamed values are rendered as integers.
- If the enum declares a method
- Zig untyped enum literal -> JSON string naming the active tag.
- Zig error -> JSON string naming the error.
- Zig
*T-> the rendering ofT. 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.
Function parameters
Parameters
- self:*Stringify
- v:anytype
Type definitions in this namespace
Types
Functions in this namespace
Functions
- An alternative to calling `write` that formats a value with `std.fmt`.
- beginWriteRaw
- An alternative to calling `write` that allows you to write directly to the `.writer` field, e.g.
- endWriteRaw
- See `beginWriteRaw`.
- objectField
- See `Stringify` for when to call this method.
- objectFieldRaw
- See `Stringify` for when to call this method.
- beginObjectFieldRaw
- In the rare case that you need to write very long object field names,
- endObjectFieldRaw
- See `beginObjectFieldRaw`.
- write
- Renders the given Zig value as JSON.
- value
- Writes the given value to the `Writer` writer.
- valueAlloc
- Calls `value` and stores the result in dynamically allocated memory instead
- encodeJsonString
- Write `string` to `writer` as a JSON encoded string.
- encodeJsonStringChars
- Write `chars` to `writer` as JSON encoded string characters.
Error sets in this namespace
Error Sets
Source
Implementation
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;
}