authorgravatar for rumcode@icloud.comChris Heyes <rumcode@icloud.com> 2021-11-15 23:56:51+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-11-15 18:56:51-05:00
log5d2a77cd9ac44df4c0e8b5affd983cae6d00ce55
tree4026836f2a22a91fec29599db41ed98e24d47d0e
parent39c0d8bd2c2bd048da6ea1821c6c46aa60fa2907
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Json Stringify option to not write out null optional fields (#8979)


1 files changed, 61 insertions(+), 15 deletions(-)

lib/std/json.zig+61-15
...@@ -2846,6 +2846,9 @@ pub const StringifyOptions = struct {...@@ -2846,6 +2846,9 @@ pub const StringifyOptions = struct {
2846 /// Controls the whitespace emitted2846 /// Controls the whitespace emitted
2847 whitespace: ?Whitespace = null,2847 whitespace: ?Whitespace = null,
28482848
2849 /// Should optional fields with null value be written?
2850 emit_null_optional_fields: bool = true,
2851
2849 string: StringOptions = StringOptions{ .String = .{} },2852 string: StringOptions = StringOptions{ .String = .{} },
28502853
2851 /// Should []u8 be serialised as a string? or an array?2854 /// Should []u8 be serialised as a string? or an array?
...@@ -2942,7 +2945,7 @@ pub fn stringify(...@@ -2942,7 +2945,7 @@ pub fn stringify(
2942 }2945 }
29432946
2944 try out_stream.writeByte('{');2947 try out_stream.writeByte('{');
2945 comptime var field_output = false;2948 var field_output = false;
2946 var child_options = options;2949 var child_options = options;
2947 if (child_options.whitespace) |*child_whitespace| {2950 if (child_options.whitespace) |*child_whitespace| {
2948 child_whitespace.indent_level += 1;2951 child_whitespace.indent_level += 1;
...@@ -2951,23 +2954,36 @@ pub fn stringify(...@@ -2951,23 +2954,36 @@ pub fn stringify(
2951 // don't include void fields2954 // don't include void fields
2952 if (Field.field_type == void) continue;2955 if (Field.field_type == void) continue;
29532956
2954 if (!field_output) {2957 var emit_field = true;
2955 field_output = true;2958
2956 } else {2959 // don't include optional fields that are null when emit_null_optional_fields is set to false
2957 try out_stream.writeByte(',');2960 if (@typeInfo(Field.field_type) == .Optional) {
2958 }2961 if (options.emit_null_optional_fields == false) {
2959 if (child_options.whitespace) |child_whitespace| {2962 if (@field(value, Field.name) == null) {
2960 try out_stream.writeByte('\n');2963 emit_field = false;
2961 try child_whitespace.outputIndent(out_stream);2964 }
2965 }
2962 }2966 }
2963 try stringify(Field.name, options, out_stream);2967
2964 try out_stream.writeByte(':');2968 if (emit_field) {
2965 if (child_options.whitespace) |child_whitespace| {2969 if (!field_output) {
2966 if (child_whitespace.separator) {2970 field_output = true;
2967 try out_stream.writeByte(' ');2971 } else {
2972 try out_stream.writeByte(',');
2973 }
2974 if (child_options.whitespace) |child_whitespace| {
2975 try out_stream.writeByte('\n');
2976 try child_whitespace.outputIndent(out_stream);
2968 }2977 }
2978 try stringify(Field.name, options, out_stream);
2979 try out_stream.writeByte(':');
2980 if (child_options.whitespace) |child_whitespace| {
2981 if (child_whitespace.separator) {
2982 try out_stream.writeByte(' ');
2983 }
2984 }
2985 try stringify(@field(value, Field.name), child_options, out_stream);
2969 }2986 }
2970 try stringify(@field(value, Field.name), child_options, out_stream);
2971 }2987 }
2972 if (field_output) {2988 if (field_output) {
2973 if (options.whitespace) |whitespace| {2989 if (options.whitespace) |whitespace| {
...@@ -3257,3 +3273,33 @@ test "stringify struct with custom stringifier" {...@@ -3257,3 +3273,33 @@ test "stringify struct with custom stringifier" {
3257test "stringify vector" {3273test "stringify vector" {
3258 try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{});3274 try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{});
3259}3275}
3276
3277test "stringify null optional fields" {
3278 const MyStruct = struct {
3279 optional: ?[]const u8 = null,
3280 required: []const u8 = "something",
3281 another_optional: ?[]const u8 = null,
3282 another_required: []const u8 = "something else",
3283 };
3284 try teststringify(
3285 \\{"optional":null,"required":"something","another_optional":null,"another_required":"something else"}
3286 ,
3287 MyStruct{},
3288 StringifyOptions{},
3289 );
3290 try teststringify(
3291 \\{"required":"something","another_required":"something else"}
3292 ,
3293 MyStruct{},
3294 StringifyOptions{ .emit_null_optional_fields = false },
3295 );
3296
3297 try std.testing.expect(try parsesTo(
3298 MyStruct,
3299 MyStruct{},
3300 &TokenStream.init(
3301 \\{"required":"something","another_required":"something else"}
3302 ),
3303 .{ .allocator = std.testing.allocator },
3304 ));
3305}