Skip to content

Commit 16bae90

Browse files
daiyippyglove authors
authored and
pyglove authors
committed
Support force_dict flag for pg.to_json/pg.save.
PiperOrigin-RevId: 624361438
1 parent 373a841 commit 16bae90

File tree

4 files changed

+48
-19
lines changed

4 files changed

+48
-19
lines changed

pyglove/core/object_utils/json_conversion.py

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ def registered_types() -> Iterable[Tuple[str, Type[JSONConvertible]]]:
336336
return JSONConvertible.registered_types()
337337

338338

339-
def to_json(value: Any, **kwargs) -> Any:
339+
def to_json(value: Any, *, force_dict: bool = False, **kwargs) -> Any:
340340
"""Serializes a (maybe) JSONConvertible value into a plain Python object.
341341
342342
Args:
@@ -348,42 +348,51 @@ def to_json(value: Any, **kwargs) -> Any:
348348
* Tuple types;
349349
* Dict types.
350350
351+
force_dict: If True, "_type" keys will be renamed to "type_name".
352+
As a result, JSONConvertible objects will be returned as dict.
351353
**kwargs: Keyword arguments to pass to value.to_json if value is
352354
JSONConvertible.
353355
354356
Returns:
355357
JSON value.
356358
"""
357359
if isinstance(value, (type(None), bool, int, float, str)):
358-
return value
360+
v = value
359361
elif isinstance(value, JSONConvertible):
360-
return value.to_json(**kwargs)
362+
v = value.to_json(**kwargs)
361363
elif isinstance(value, tuple):
362-
return [JSONConvertible.TUPLE_MARKER] + to_json(list(value), **kwargs)
364+
v = [JSONConvertible.TUPLE_MARKER] + to_json(list(value), **kwargs)
363365
elif isinstance(value, list):
364-
return [to_json(item, **kwargs) for item in value]
366+
v = [to_json(item, **kwargs) for item in value]
365367
elif isinstance(value, dict):
366-
return {k: to_json(v, **kwargs) for k, v in value.items()}
368+
v = {k: to_json(v, **kwargs) for k, v in value.items()}
367369
elif isinstance(value, (type, typing.GenericAlias)): # pytype: disable=module-attr
368-
return _type_to_json(value)
370+
v = _type_to_json(value)
369371
elif inspect.isbuiltin(value):
370-
return _builtin_function_to_json(value)
372+
v = _builtin_function_to_json(value)
371373
elif inspect.isfunction(value):
372-
return _function_to_json(value)
374+
v = _function_to_json(value)
373375
elif inspect.ismethod(value):
374-
return _method_to_json(value)
376+
v = _method_to_json(value)
375377
# pytype: disable=module-attr
376378
elif isinstance(value, typing._Final): # pylint: disable=protected-access
377379
# pytype: enable=module-attr
378-
return _annotation_to_json(value)
380+
v = _annotation_to_json(value)
379381
elif value is ...:
380-
return {JSONConvertible.TYPE_NAME_KEY: 'type', 'name': 'builtins.Ellipsis'}
382+
v = {JSONConvertible.TYPE_NAME_KEY: 'type', 'name': 'builtins.Ellipsis'}
381383
else:
384+
v, converted = None, False
382385
if JSONConvertible.TYPE_CONVERTER is not None:
383386
converter = JSONConvertible.TYPE_CONVERTER(type(value)) # pylint: disable=not-callable
384387
if converter:
385-
return to_json(converter(value))
386-
return _OpaqueObject(value).to_json(**kwargs)
388+
v = to_json(converter(value))
389+
converted = True
390+
if not converted:
391+
v = _OpaqueObject(value).to_json(**kwargs)
392+
393+
if force_dict:
394+
v = replace_type_with_type_names(v)
395+
return v
387396

388397

389398
def from_json(json_value: JSONValueType,

pyglove/core/object_utils/json_conversion_test.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ class LocalX:
298298
json_conversion.from_json(json_dict)
299299

300300
def test_json_conversion_force_dict(self):
301+
self.assertEqual(
302+
json_conversion.to_json([int], force_dict=True),
303+
[{'name': 'builtins.int', 'type_name': 'type'}]
304+
)
301305
self.assertEqual(
302306
json_conversion.from_json([
303307
'__tuple__',

pyglove/core/symbolic/base.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -952,11 +952,11 @@ def clone(
952952

953953
def to_json(self, **kwargs) -> object_utils.JSONValueType:
954954
"""Alias for `sym_jsonify`."""
955-
return self.sym_jsonify(**kwargs)
955+
return to_json(self, **kwargs)
956956

957957
def to_json_str(self, json_indent: Optional[int] = None, **kwargs) -> str:
958958
"""Serializes current object into a JSON string."""
959-
return json.dumps(self.sym_jsonify(**kwargs), indent=json_indent)
959+
return to_json_str(self, json_indent=json_indent, **kwargs)
960960

961961
@classmethod
962962
def load(cls, *args, **kwargs) -> Any:
@@ -2114,7 +2114,7 @@ class A(pg.Object):
21142114
**kwargs)
21152115

21162116

2117-
def to_json(value: Any, **kwargs) -> Any:
2117+
def to_json(value: Any, *, force_dict: bool = False, **kwargs) -> Any:
21182118
"""Serializes a (maybe) symbolic value into a plain Python object.
21192119
21202120
Example::
@@ -2139,6 +2139,8 @@ class A(pg.Object):
21392139
* Tuple types;
21402140
* Dict types.
21412141
2142+
force_dict: If True, "_type" keys will be renamed to "type_name".
2143+
As a result, JSONConvertible objects will be returned as dict.
21422144
**kwargs: Keyword arguments to pass to value.to_json if value is
21432145
JSONConvertible.
21442146
@@ -2148,8 +2150,11 @@ class A(pg.Object):
21482150
# NOTE(daiyip): special handling `sym_jsonify` since symbolized
21492151
# classes may have conflicting `to_json` method in their existing classes.
21502152
if isinstance(value, Symbolic):
2151-
return value.sym_jsonify(**kwargs)
2152-
return object_utils.to_json(value, **kwargs)
2153+
v = value.sym_jsonify(**kwargs)
2154+
if force_dict:
2155+
v = object_utils.json_conversion.replace_type_with_type_names(v)
2156+
return v
2157+
return object_utils.to_json(value, force_dict=force_dict, **kwargs)
21532158

21542159

21552160
def to_json_str(value: Any,

pyglove/core/symbolic/object_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2944,6 +2944,17 @@ class Q(Object):
29442944
p: P
29452945
y: str
29462946

2947+
self.assertEqual(
2948+
Q(P(1), y='foo').to_json(force_dict=True),
2949+
{
2950+
'p': {
2951+
'type_name': P.__type_name__,
2952+
'x': 1
2953+
},
2954+
'y': 'foo',
2955+
'type_name': Q.__type_name__,
2956+
}
2957+
)
29472958
self.assertEqual(
29482959
base.from_json_str(Q(P(1), y='foo').to_json_str(), force_dict=True),
29492960
{

0 commit comments

Comments
 (0)