Commit 781a21af2f
Changed files (1)
lib
std
lib/std/json.zig
@@ -1105,6 +1105,10 @@ pub const TokenStream = struct {
};
}
+ fn stackUsed(self: *TokenStream) u8 {
+ return self.parser.stack_used + if (self.token != null) @as(u8, 1) else 0;
+ }
+
pub fn next(self: *TokenStream) Error!?Token {
if (self.token) |token| {
self.token = null;
@@ -1457,8 +1461,73 @@ pub const ParseOptions = struct {
Error,
UseLast,
} = .Error,
+
+ /// If false, finding an unknown field returns an error.
+ ignore_unknown_fields: bool = false,
};
+fn skipValue(tokens: *TokenStream) !void {
+ const original_depth = tokens.stackUsed();
+
+ // Return an error if no value is found
+ _ = try tokens.next();
+ if (tokens.stackUsed() < original_depth) return error.UnexpectedJsonDepth;
+ if (tokens.stackUsed() == original_depth) return;
+
+ while (try tokens.next()) |_| {
+ if (tokens.stackUsed() == original_depth) return;
+ }
+}
+
+test "skipValue" {
+ try skipValue(&TokenStream.init("false"));
+ try skipValue(&TokenStream.init("true"));
+ try skipValue(&TokenStream.init("null"));
+ try skipValue(&TokenStream.init("42"));
+ try skipValue(&TokenStream.init("42.0"));
+ try skipValue(&TokenStream.init("\"foo\""));
+ try skipValue(&TokenStream.init("[101, 111, 121]"));
+ try skipValue(&TokenStream.init("{}"));
+ try skipValue(&TokenStream.init("{\"foo\": \"bar\"}"));
+
+ { // An absurd number of nestings
+ const nestings = 256;
+
+ testing.expectError(
+ error.TooManyNestedItems,
+ skipValue(&TokenStream.init("[" ** nestings ++ "]" ** nestings)),
+ );
+ }
+
+ { // Would a number token cause problems in a deeply-nested array?
+ const nestings = 255;
+ const deeply_nested_array = "[" ** nestings ++ "0.118, 999, 881.99, 911.9, 725, 3" ++ "]" ** nestings;
+
+ try skipValue(&TokenStream.init(deeply_nested_array));
+
+ testing.expectError(
+ error.TooManyNestedItems,
+ skipValue(&TokenStream.init("[" ++ deeply_nested_array ++ "]")),
+ );
+ }
+
+ // Mismatched brace/square bracket
+ testing.expectError(
+ error.UnexpectedClosingBrace,
+ skipValue(&TokenStream.init("[102, 111, 111}")),
+ );
+
+ { // should fail if no value found (e.g. immediate close of object)
+ var empty_object = TokenStream.init("{}");
+ assert(.ObjectBegin == (try empty_object.next()).?);
+ testing.expectError(error.UnexpectedJsonDepth, skipValue(&empty_object));
+
+ var empty_array = TokenStream.init("[]");
+ assert(.ArrayBegin == (try empty_array.next()).?);
+ testing.expectError(error.UnexpectedJsonDepth, skipValue(&empty_array));
+ }
+}
+
fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options: ParseOptions) !T {
switch (@typeInfo(T)) {
.Bool => {
@@ -1598,7 +1667,14 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
break;
}
}
- if (!found) return error.UnknownField;
+ if (!found) {
+ if (options.ignore_unknown_fields) {
+ try skipValue(tokens);
+ continue;
+ } else {
+ return error.UnknownField;
+ }
+ }
},
else => return error.UnexpectedToken,
}
@@ -2040,6 +2116,46 @@ test "parse into struct with duplicate field" {
try testing.expectError(error.UnexpectedValue, parse(T3, &TokenStream.init(str), options_last));
}
+test "parse into struct ignoring unknown fields" {
+ const T = struct {
+ int: i64,
+ language: []const u8,
+ };
+
+ const ops = ParseOptions{
+ .allocator = testing.allocator,
+ .ignore_unknown_fields = true,
+ };
+
+ const r = try parse(T, &std.json.TokenStream.init(
+ \\{
+ \\ "int": 420,
+ \\ "float": 3.14,
+ \\ "with\\escape": true,
+ \\ "with\u0105unicode\ud83d\ude02": false,
+ \\ "optional": null,
+ \\ "static_array": [66.6, 420.420, 69.69],
+ \\ "dynamic_array": [66.6, 420.420, 69.69],
+ \\ "complex": {
+ \\ "nested": "zig"
+ \\ },
+ \\ "veryComplex": [
+ \\ {
+ \\ "foo": "zig"
+ \\ }, {
+ \\ "foo": "rocks"
+ \\ }
+ \\ ],
+ \\ "a_union": 100000,
+ \\ "language": "zig"
+ \\}
+ ), ops);
+ defer parseFree(T, r, ops);
+
+ testing.expectEqual(@as(i64, 420), r.int);
+ testing.expectEqualSlices(u8, "zig", r.language);
+}
+
/// A non-stream JSON parser which constructs a tree of Value's.
pub const Parser = struct {
allocator: *Allocator,