| ... | ... | @@ -2846,6 +2846,9 @@ pub const StringifyOptions = struct { |
| 2846 | 2846 | /// Controls the whitespace emitted |
| 2847 | 2847 | whitespace: ?Whitespace = null, |
| 2848 | 2848 | |
| 2849 | /// Should optional fields with null value be written? |
| 2850 | emit_null_optional_fields: bool = true, |
| 2851 | |
| 2849 | 2852 | string: StringOptions = StringOptions{ .String = .{} }, |
| 2850 | 2853 | |
| 2851 | 2854 | /// Should []u8 be serialised as a string? or an array? |
| ... | ... | @@ -2942,7 +2945,7 @@ pub fn stringify( |
| 2942 | 2945 | } |
| 2943 | 2946 | |
| 2944 | 2947 | try out_stream.writeByte('{'); |
| 2945 | | comptime var field_output = false; |
| 2948 | var field_output = false; |
| 2946 | 2949 | var child_options = options; |
| 2947 | 2950 | if (child_options.whitespace) |*child_whitespace| { |
| 2948 | 2951 | child_whitespace.indent_level += 1; |
| ... | ... | @@ -2951,23 +2954,36 @@ pub fn stringify( |
| 2951 | 2954 | // don't include void fields |
| 2952 | 2955 | if (Field.field_type == void) continue; |
| 2953 | 2956 | |
| 2954 | | if (!field_output) { |
| 2955 | | field_output = true; |
| 2956 | | } else { |
| 2957 | | try out_stream.writeByte(','); |
| 2958 | | } |
| 2959 | | if (child_options.whitespace) |child_whitespace| { |
| 2960 | | try out_stream.writeByte('\n'); |
| 2961 | | try child_whitespace.outputIndent(out_stream); |
| 2957 | var emit_field = true; |
| 2958 | |
| 2959 | // don't include optional fields that are null when emit_null_optional_fields is set to false |
| 2960 | if (@typeInfo(Field.field_type) == .Optional) { |
| 2961 | if (options.emit_null_optional_fields == false) { |
| 2962 | if (@field(value, Field.name) == null) { |
| 2963 | emit_field = false; |
| 2964 | } |
| 2965 | } |
| 2962 | 2966 | } |
| 2963 | | try stringify(Field.name, options, out_stream); |
| 2964 | | try out_stream.writeByte(':'); |
| 2965 | | if (child_options.whitespace) |child_whitespace| { |
| 2966 | | if (child_whitespace.separator) { |
| 2967 | | try out_stream.writeByte(' '); |
| 2967 | |
| 2968 | if (emit_field) { |
| 2969 | if (!field_output) { |
| 2970 | field_output = true; |
| 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 | 2988 | if (field_output) { |
| 2973 | 2989 | if (options.whitespace) |whitespace| { |
| ... | ... | @@ -3257,3 +3273,33 @@ test "stringify struct with custom stringifier" { |
| 3257 | 3273 | test "stringify vector" { |
| 3258 | 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 | } |