From 9e8b3a6fc60ce5bb8a8c8e1e9e1e65360d0a25ee Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Fri, 21 Mar 2025 09:59:33 -0700 Subject: [PATCH] fix json serialization for null/empty maps --- codegen/src/json.zig | 20 +++++++++++++++-- src/aws.zig | 52 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/codegen/src/json.zig b/codegen/src/json.zig index f3f4f47..3fe93ec 100644 --- a/codegen/src/json.zig +++ b/codegen/src/json.zig @@ -4,7 +4,7 @@ const std = @import("std"); pub fn serializeMap(map: anytype, key: []const u8, options: anytype, out_stream: anytype) !bool { if (@typeInfo(@TypeOf(map)) == .optional) { if (map == null) - return true + return false else return serializeMapInternal(map.?, key, options, out_stream); } @@ -12,7 +12,23 @@ pub fn serializeMap(map: anytype, key: []const u8, options: anytype, out_stream: } fn serializeMapInternal(map: anytype, key: []const u8, options: anytype, out_stream: anytype) !bool { - if (map.len == 0) return true; + if (map.len == 0) { + var child_options = options; + if (child_options.whitespace) |*child_ws| + child_ws.indent_level += 1; + + try out_stream.writeByte('"'); + try out_stream.writeAll(key); + _ = try out_stream.write("\":"); + if (options.whitespace) |ws| { + if (ws.separator) { + try out_stream.writeByte(' '); + } + } + try out_stream.writeByte('{'); + try out_stream.writeByte('}'); + return true; + } // TODO: Map might be [][]struct{key, value} rather than []struct{key, value} var child_options = options; if (child_options.whitespace) |*child_ws| diff --git a/src/aws.zig b/src/aws.zig index 424e7b2..2738a1e 100644 --- a/src/aws.zig +++ b/src/aws.zig @@ -1310,6 +1310,58 @@ test "custom serialization for map objects" { , buffer.items); } +test "proper serialization for kms" { + // Github issue #8 + // https://github.com/elerch/aws-sdk-for-zig/issues/8 + const allocator = std.testing.allocator; + var buffer = std.ArrayList(u8).init(allocator); + defer buffer.deinit(); + const req = services.kms.encrypt.Request{ + .encryption_algorithm = "SYMMETRIC_DEFAULT", + // Since encryption_context is not null, we expect "{}" to be the value + // here, not "[]", because this is our special AWS map pattern + .encryption_context = &.{}, + .key_id = "42", + .plaintext = "foo", + .dry_run = false, + .grant_tokens = &[_][]const u8{}, + }; + try json.stringify(req, .{ .whitespace = .{} }, buffer.writer()); + try std.testing.expectEqualStrings( + \\{ + \\ "KeyId": "42", + \\ "Plaintext": "foo", + \\ "EncryptionContext": {}, + \\ "GrantTokens": [], + \\ "EncryptionAlgorithm": "SYMMETRIC_DEFAULT", + \\ "DryRun": false + \\} + , buffer.items); + + var buffer_null = std.ArrayList(u8).init(allocator); + defer buffer_null.deinit(); + const req_null = services.kms.encrypt.Request{ + .encryption_algorithm = "SYMMETRIC_DEFAULT", + // Since encryption_context here *IS* null, we expect simply "null" to be the value + .encryption_context = null, + .key_id = "42", + .plaintext = "foo", + .dry_run = false, + .grant_tokens = &[_][]const u8{}, + }; + try json.stringify(req_null, .{ .whitespace = .{} }, buffer_null.writer()); + try std.testing.expectEqualStrings( + \\{ + \\ "KeyId": "42", + \\ "Plaintext": "foo", + \\ "EncryptionContext": null, + \\ "GrantTokens": [], + \\ "EncryptionAlgorithm": "SYMMETRIC_DEFAULT", + \\ "DryRun": false + \\} + , buffer_null.items); +} + test "REST Json v1 builds proper queries" { const allocator = std.testing.allocator; const svs = Services(.{.lambda}){};