Skip to content

Commit c08debc

Browse files
haranrkcopybara-github
authored andcommitted
fix: recurse into JSON-native containers in A2A _serialize_value()
The previous change passed `dict` and `list` values through `_serialize_value()` unchanged. A non-JSON-serializable value nested inside one of those containers (e.g. a `datetime`, `uuid.UUID`, or a nested Pydantic model) was therefore returned raw. That is inconsistent with the stated intent that only non-JSON-serializable types are stringified, and it risks a `TypeError` when the A2A metadata is later JSON-encoded. Recurse into `dict` and `list` elements so nested non-JSON-serializable values are still stringified, while JSON-native scalars continue to pass through as-is. Co-authored-by: Haran Rajkumar <haranrk@google.com> PiperOrigin-RevId: 935043902
1 parent 25d666a commit c08debc

2 files changed

Lines changed: 47 additions & 2 deletions

File tree

src/google/adk/a2a/converters/from_adk_event.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,19 @@ def _serialize_value(value: Any) -> Optional[Any]:
259259
logger.warning("Failed to serialize Pydantic model, falling back: %s", e)
260260
return str(value)
261261

262-
# Pass through JSON-native types as-is
263-
if isinstance(value, (dict, list, int, float, bool, str)):
262+
# Recurse into JSON-native containers so nested non-JSON-serializable
263+
# values (e.g. datetime) are still stringified, then pass through other
264+
# JSON-native scalars as-is.
265+
if isinstance(value, dict):
266+
# JSON object keys must be strings, so stringify any non-string key to
267+
# avoid a downstream TypeError when the metadata is JSON-encoded.
268+
return {
269+
(k if isinstance(k, str) else str(k)): _serialize_value(v)
270+
for k, v in value.items()
271+
}
272+
if isinstance(value, list):
273+
return [_serialize_value(item) for item in value]
274+
if isinstance(value, (int, float, bool, str)):
264275
return value
265276

266277
return str(value)

tests/unittests/a2a/converters/test_from_adk.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,37 @@ def test_non_json_type_stringified(self) -> None:
179179
dt = datetime(2025, 1, 1)
180180
result = self.serialize(dt)
181181
assert isinstance(result, str)
182+
183+
def test_nested_non_json_value_in_dict_stringified(self) -> None:
184+
"""A non-JSON-native value nested in a dict is stringified."""
185+
from datetime import datetime
186+
187+
dt = datetime(2025, 1, 1)
188+
value = {"when": dt, "count": 1, "label": "x"}
189+
result = self.serialize(value)
190+
assert result == {"when": str(dt), "count": 1, "label": "x"}
191+
assert isinstance(result["when"], str)
192+
assert isinstance(result["count"], int)
193+
assert isinstance(result["label"], str)
194+
195+
def test_nested_non_json_value_in_list_stringified(self) -> None:
196+
"""A non-JSON-native value nested in a list is stringified."""
197+
from datetime import datetime
198+
199+
dt = datetime(2025, 1, 1)
200+
value = [dt, 1, "x"]
201+
result = self.serialize(value)
202+
assert result == [str(dt), 1, "x"]
203+
assert isinstance(result[0], str)
204+
assert isinstance(result[1], int)
205+
assert isinstance(result[2], str)
206+
207+
def test_non_string_dict_key_stringified(self) -> None:
208+
"""Non-string dict keys are stringified so the result is JSON-encodable."""
209+
from datetime import datetime
210+
211+
dt = datetime(2025, 1, 1)
212+
value = {dt: "when", 1: "one", "label": "x"}
213+
result = self.serialize(value)
214+
assert result == {str(dt): "when", "1": "one", "label": "x"}
215+
assert all(isinstance(k, str) for k in result)

0 commit comments

Comments
 (0)