authorgravatar for 144539106+eugene-dash@users.noreply.github.comEugene-Dash <144539106+eugene-dash@users.noreply.github.com> 2024-07-25 20:55:06-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-07-25 20:55:06-04:00
log81a172a5064559d1f968f240204f8ce545852441
treeddb1b9b2f70114f8c2e1e99319151922834a7176
parented847b85c284783c20efd4c4a565138b79d33e2c
signaturebadge-check Signed by PGP key B5690EEEBB952194

Add `std.json.ParseOptions.parse_numbers` to preserve float precision (#20744)


3 files changed, 28 insertions(+), 1 deletions(-)

lib/std/json/dynamic.zig+6-1
...@@ -22,6 +22,7 @@ pub const Array = ArrayList(Value);...@@ -22,6 +22,7 @@ pub const Array = ArrayList(Value);
22/// Represents any JSON value, potentially containing other JSON values.22/// Represents any JSON value, potentially containing other JSON values.
23/// A .float value may be an approximation of the original value.23/// A .float value may be an approximation of the original value.
24/// Arbitrary precision numbers can be represented by .number_string values.24/// Arbitrary precision numbers can be represented by .number_string values.
25/// See also `std.json.ParseOptions.parse_numbers`.
25pub const Value = union(enum) {26pub const Value = union(enum) {
26 null,27 null,
27 bool: bool,28 bool: bool,
...@@ -97,7 +98,11 @@ pub const Value = union(enum) {...@@ -97,7 +98,11 @@ pub const Value = union(enum) {
97 return try handleCompleteValue(&stack, allocator, source, Value{ .string = s }, options) orelse continue;98 return try handleCompleteValue(&stack, allocator, source, Value{ .string = s }, options) orelse continue;
98 },99 },
99 .allocated_number => |slice| {100 .allocated_number => |slice| {
100 return try handleCompleteValue(&stack, allocator, source, Value.parseFromNumberSlice(slice), options) orelse continue;101 if (options.parse_numbers) {
102 return try handleCompleteValue(&stack, allocator, source, Value.parseFromNumberSlice(slice), options) orelse continue;
103 } else {
104 return try handleCompleteValue(&stack, allocator, source, Value{ .number_string = slice }, options) orelse continue;
105 }
101 },106 },
102107
103 .null => return try handleCompleteValue(&stack, allocator, source, .null, options) orelse continue,108 .null => return try handleCompleteValue(&stack, allocator, source, .null, options) orelse continue,
lib/std/json/dynamic_test.zig+13
...@@ -149,6 +149,19 @@ test "integer after float has proper type" {...@@ -149,6 +149,19 @@ test "integer after float has proper type" {
149 try std.testing.expect(parsed.object.get("ints").?.array.items[0] == .integer);149 try std.testing.expect(parsed.object.get("ints").?.array.items[0] == .integer);
150}150}
151151
152test "ParseOptions.parse_numbers prevents parsing when false" {
153 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
154 defer arena_allocator.deinit();
155 const parsed = try parseFromSliceLeaky(Value, arena_allocator.allocator(),
156 \\{
157 \\ "float": 3.14,
158 \\ "int": 3
159 \\}
160 , .{ .parse_numbers = false });
161 try std.testing.expect(parsed.object.get("float").? == .number_string);
162 try std.testing.expect(parsed.object.get("int").? == .number_string);
163}
164
152test "escaped characters" {165test "escaped characters" {
153 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);166 var arena_allocator = std.heap.ArenaAllocator.init(std.testing.allocator);
154 defer arena_allocator.deinit();167 defer arena_allocator.deinit();
lib/std/json/static.zig+9
...@@ -42,6 +42,15 @@ pub const ParseOptions = struct {...@@ -42,6 +42,15 @@ pub const ParseOptions = struct {
42 /// The default with a `*std.json.Reader` input is `.alloc_always`.42 /// The default with a `*std.json.Reader` input is `.alloc_always`.
43 /// Ignored for `parseFromValue` and `parseFromValueLeaky`.43 /// Ignored for `parseFromValue` and `parseFromValueLeaky`.
44 allocate: ?AllocWhen = null,44 allocate: ?AllocWhen = null,
45
46 /// When parsing to a `std.json.Value`, set this option to false to always emit
47 /// JSON numbers as unparsed `std.json.Value.number_string`.
48 /// Otherwise, JSON numbers are parsed as either `std.json.Value.integer`,
49 /// `std.json.Value.float` or left as unparsed `std.json.Value.number_string`
50 /// depending on the format and value of the JSON number.
51 /// When this option is true, JSON numbers encoded as floats (see `std.json.isNumberFormattedLikeAnInteger`)
52 /// may lose precision when being parsed into `std.json.Value.float`.
53 parse_numbers: bool = true,
45};54};
4655
47pub fn Parsed(comptime T: type) type {56pub fn Parsed(comptime T: type) type {