innerParseFromValue
This is an internal function called recursively
during the implementation of parseFromValueLeaky.
It is exposed primarily to enable custom jsonParseFromValue() methods to call back into the parseFromValue* system,
such as if you're implementing a custom container of type T;
you can call innerParseFromValue(T, ...) for each of the container's items.
Function parameters
Parameters
Controls how to deal with various inconsistencies between the JSON document and the Zig struct type passed in.
Types
- ParseOptions
- Controls how to deal with various inconsistencies between the JSON document and the Zig struct type passed in.
Functions in this namespace
Functions
- parseFromSlice
- Parses the json document from `s` and returns the result packaged in a `std.json.Parsed`.
- parseFromSliceLeaky
- Parses the json document from `s` and returns the result.
- parseFromTokenSource
- `scanner_or_reader` must be either a `*std.json.Scanner` with complete input or a `*std.json.Reader`.
- parseFromTokenSourceLeaky
- `scanner_or_reader` must be either a `*std.json.Scanner` with complete input or a `*std.json.Reader`.
- parseFromValue
- Like `parseFromSlice`, but the input is an already-parsed `std.json.Value` object.
- ParseError
- The error set that will be returned when parsing from `*Source`.
- innerParse
- This is an internal function called recursively
- innerParseFromValue
- This is an internal function called recursively
Error sets in this namespace
Error Sets
Source
Implementation
pub fn innerParseFromValue(
comptime T: type,
allocator: Allocator,
source: Value,
options: ParseOptions,
) ParseFromValueError!T {
switch (@typeInfo(T)) {
.bool => {
switch (source) {
.bool => |b| return b,
else => return error.UnexpectedToken,
}
},
.float, .comptime_float => {
switch (source) {
.float => |f| return @as(T, @floatCast(f)),
.integer => |i| return @as(T, @floatFromInt(i)),
.number_string, .string => |s| return std.fmt.parseFloat(T, s),
else => return error.UnexpectedToken,
}
},
.int, .comptime_int => {
switch (source) {
.float => |f| {
if (@round(f) != f) return error.InvalidNumber;
if (f > @as(@TypeOf(f), @floatFromInt(std.math.maxInt(T)))) return error.Overflow;
if (f < @as(@TypeOf(f), @floatFromInt(std.math.minInt(T)))) return error.Overflow;
return @as(T, @intFromFloat(f));
},
.integer => |i| {
if (i > std.math.maxInt(T)) return error.Overflow;
if (i < std.math.minInt(T)) return error.Overflow;
return @as(T, @intCast(i));
},
.number_string, .string => |s| {
return sliceToInt(T, s);
},
else => return error.UnexpectedToken,
}
},
.optional => |optionalInfo| {
switch (source) {
.null => return null,
else => return try innerParseFromValue(optionalInfo.child, allocator, source, options),
}
},
.@"enum" => {
if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options);
}
switch (source) {
.float => return error.InvalidEnumTag,
.integer => |i| return std.enums.fromInt(T, i) orelse return error.InvalidEnumTag,
.number_string, .string => |s| return sliceToEnum(T, s),
else => return error.UnexpectedToken,
}
},
.@"union" => |unionInfo| {
if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options);
}
if (unionInfo.tag_type == null) @compileError("Unable to parse into untagged union '" ++ @typeName(T) ++ "'");
if (source != .object) return error.UnexpectedToken;
if (source.object.count() != 1) return error.UnexpectedToken;
var it = source.object.iterator();
const kv = it.next().?;
const field_name = kv.key_ptr.*;
inline for (unionInfo.fields) |u_field| {
if (std.mem.eql(u8, u_field.name, field_name)) {
if (u_field.type == void) {
// void isn't really a json type, but we can support void payload union tags with {} as a value.
if (kv.value_ptr.* != .object) return error.UnexpectedToken;
if (kv.value_ptr.*.object.count() != 0) return error.UnexpectedToken;
return @unionInit(T, u_field.name, {});
}
// Recurse.
return @unionInit(T, u_field.name, try innerParseFromValue(u_field.type, allocator, kv.value_ptr.*, options));
}
}
// Didn't match anything.
return error.UnknownField;
},
.@"struct" => |structInfo| {
if (structInfo.is_tuple) {
if (source != .array) return error.UnexpectedToken;
if (source.array.items.len != structInfo.fields.len) return error.UnexpectedToken;
var r: T = undefined;
inline for (0..structInfo.fields.len, source.array.items) |i, item| {
r[i] = try innerParseFromValue(structInfo.fields[i].type, allocator, item, options);
}
return r;
}
if (std.meta.hasFn(T, "jsonParseFromValue")) {
return T.jsonParseFromValue(allocator, source, options);
}
if (source != .object) return error.UnexpectedToken;
var r: T = undefined;
var fields_seen = [_]bool{false} ** structInfo.fields.len;
var it = source.object.iterator();
while (it.next()) |kv| {
const field_name = kv.key_ptr.*;
inline for (structInfo.fields, 0..) |field, i| {
if (field.is_comptime) @compileError("comptime fields are not supported: " ++ @typeName(T) ++ "." ++ field.name);
if (std.mem.eql(u8, field.name, field_name)) {
assert(!fields_seen[i]); // Can't have duplicate keys in a Value.object.
@field(r, field.name) = try innerParseFromValue(field.type, allocator, kv.value_ptr.*, options);
fields_seen[i] = true;
break;
}
} else {
// Didn't match anything.
if (!options.ignore_unknown_fields) return error.UnknownField;
}
}
try fillDefaultStructValues(T, &r, &fields_seen);
return r;
},
.array => |arrayInfo| {
switch (source) {
.array => |array| {
// Typical array.
return innerParseArrayFromArrayValue(T, arrayInfo.child, arrayInfo.len, allocator, array, options);
},
.string => |s| {
if (arrayInfo.child != u8) return error.UnexpectedToken;
// Fixed-length string.
if (s.len != arrayInfo.len) return error.LengthMismatch;
var r: T = undefined;
@memcpy(r[0..], s);
return r;
},
else => return error.UnexpectedToken,
}
},
.vector => |vecInfo| {
switch (source) {
.array => |array| {
return innerParseArrayFromArrayValue(T, vecInfo.child, vecInfo.len, allocator, array, options);
},
else => return error.UnexpectedToken,
}
},
.pointer => |ptrInfo| {
switch (ptrInfo.size) {
.one => {
const r: *ptrInfo.child = try allocator.create(ptrInfo.child);
r.* = try innerParseFromValue(ptrInfo.child, allocator, source, options);
return r;
},
.slice => {
switch (source) {
.array => |array| {
const r = if (ptrInfo.sentinel()) |sentinel|
try allocator.allocSentinel(ptrInfo.child, array.items.len, sentinel)
else
try allocator.alloc(ptrInfo.child, array.items.len);
for (array.items, r) |item, *dest| {
dest.* = try innerParseFromValue(ptrInfo.child, allocator, item, options);
}
return r;
},
.string => |s| {
if (ptrInfo.child != u8) return error.UnexpectedToken;
// Dynamic length string.
const r = if (ptrInfo.sentinel()) |sentinel|
try allocator.allocSentinel(ptrInfo.child, s.len, sentinel)
else
try allocator.alloc(ptrInfo.child, s.len);
@memcpy(r[0..], s);
return r;
},
else => return error.UnexpectedToken,
}
},
else => @compileError("Unable to parse into type '" ++ @typeName(T) ++ "'"),
}
},
else => @compileError("Unable to parse into type '" ++ @typeName(T) ++ "'"),
}
}