From 47dc31b7c0c38b6ef8cb1a2250e12761e3b698c1 Mon Sep 17 00:00:00 2001 From: Jeff Mesnil Date: Thu, 26 Mar 2026 17:00:13 +0100 Subject: [PATCH] fix: omit optional fields with default values from client JSON serialization The JSON-RPC client serialization used alwaysPrintFieldsWithNoPresence() which caused proto3 implicit-presence fields (taskId, contextId, tenant, etc.) to be serialized as empty strings instead of being omitted. Removed alwaysPrintFieldsWithNoPresence() from JsonFormat.printer() in JSONRPCUtils.toJsonRPCRequest() (client requests only), aligning with the REST client transport which already omitted it. This fixes #770 --- .../transport/jsonrpc/JsonMessages.java | 59 ++++--------------- .../java/io/a2a/grpc/utils/JSONRPCUtils.java | 7 ++- 2 files changed, 16 insertions(+), 50 deletions(-) diff --git a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java index a0be30e0d..e7cb206f2 100644 --- a/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java +++ b/client/transport/jsonrpc/src/test/java/io/a2a/client/transport/jsonrpc/JsonMessages.java @@ -93,10 +93,7 @@ public class JsonMessages { { "text":"tell me a joke" } - ], - "metadata":{ - - } + ] } } }"""; @@ -143,18 +140,12 @@ public class JsonMessages { { "text":"tell me a joke" } - ], - "metadata":{ - } + ] }, "configuration":{ "acceptedOutputModes":[ "text" - ], - "returnImmediately":false - }, - "metadata":{ - + ] } } }"""; @@ -192,19 +183,12 @@ public class JsonMessages { { "text":"tell me a joke" } - ], - "metadata":{ - - } + ] }, "configuration":{ "acceptedOutputModes":[ "text" - ], - "returnImmediately":false - }, - "metadata":{ - + ] } } }"""; @@ -376,19 +360,12 @@ public class JsonMessages { "url":"file:///path/to/image.jpg", "mediaType":"image/jpeg" } - ], - "metadata":{ - - } + ] }, "configuration":{ "acceptedOutputModes":[ "text" - ], - "returnImmediately":false - }, - "metadata":{ - + ] } } }"""; @@ -443,19 +420,12 @@ public class JsonMessages { "timestamp":"2024-01-15T10:30:00Z" } } - ], - "metadata":{ - - } + ] }, "configuration":{ "acceptedOutputModes":[ "text" - ], - "returnImmediately":false - }, - "metadata":{ - + ] } } }"""; @@ -514,19 +484,12 @@ public class JsonMessages { "labels":["Q1", "Q2", "Q3", "Q4"] } } - ], - "metadata":{ - - } + ] }, "configuration":{ "acceptedOutputModes":[ "text" - ], - "returnImmediately":false - }, - "metadata":{ - + ] } } }"""; diff --git a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java index a21008bda..9e812d0bd 100644 --- a/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java +++ b/spec-grpc/src/main/java/io/a2a/grpc/utils/JSONRPCUtils.java @@ -171,6 +171,9 @@ public class JSONRPCUtils { private static final Pattern EXTRACT_WRONG_TYPE = Pattern.compile("Expected (.*) but found \".*\""); static final String ERROR_MESSAGE = "Invalid request content: %s. Please verify the request matches the expected schema for this method."; + private static final JsonFormat.Printer REQUEST_PRINTER = JsonFormat.printer().omittingInsignificantWhitespace(); + private static final JsonFormat.Printer RESPONSE_PRINTER = JsonFormat.printer().alwaysPrintFieldsWithNoPresence().omittingInsignificantWhitespace(); + public static A2ARequest parseRequestBody(String body, @Nullable String tenant) throws JsonMappingException, JsonProcessingException { JsonElement jelement = JsonParser.parseString(body); JsonObject jsonRpc = jelement.getAsJsonObject(); @@ -556,7 +559,7 @@ public static String toJsonRPCRequest(@Nullable String requestId, String method, output.name("method").value(method); } if (payload != null) { - String resultValue = JsonFormat.printer().alwaysPrintFieldsWithNoPresence().omittingInsignificantWhitespace().print(payload); + String resultValue = REQUEST_PRINTER.print(payload); output.name("params").jsonValue(resultValue); } output.endObject(); @@ -579,7 +582,7 @@ public static String toJsonRPCResultResponse(Object requestId, com.google.protob output.name("id").value(number.longValue()); } } - String resultValue = JsonFormat.printer().alwaysPrintFieldsWithNoPresence().omittingInsignificantWhitespace().print(builder); + String resultValue = RESPONSE_PRINTER.print(builder); output.name("result").jsonValue(resultValue); output.endObject(); return result.toString();