| ... | @@ -2846,6 +2846,9 @@ pub const StringifyOptions = struct { | ... | @@ -2846,6 +2846,9 @@ pub const StringifyOptions = struct { |
| 2846 | /// Controls the whitespace emitted | 2846 | /// Controls the whitespace emitted |
| 2847 | whitespace: ?Whitespace = null, | 2847 | whitespace: ?Whitespace = null, |
| 2848 | | 2848 | |
| | 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 = .{} }, |
| 2850 | | 2853 | |
| 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 | } |
| 2943 | | 2946 | |
| 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 fields | 2954 | // don't include void fields |
| 2952 | if (Field.field_type == void) continue; | 2955 | if (Field.field_type == void) continue; |
| 2953 | | 2956 | |
| 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" { |
| 3257 | test "stringify vector" { | 3273 | test "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 | |
| | 3277 | test "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 | } |