@@ -1306,6 +1306,136 @@ def from_json(self: T, value: Union[str, bytes]) -> T:
1306
1306
"""
1307
1307
return self .from_dict (json .loads (value ))
1308
1308
1309
+ def to_pydict (
1310
+ self , casing : Casing = Casing .CAMEL , include_default_values : bool = False
1311
+ ) -> Dict [str , Any ]:
1312
+ """
1313
+ Returns a python dict representation of this object.
1314
+
1315
+ Parameters
1316
+ -----------
1317
+ casing: :class:`Casing`
1318
+ The casing to use for key values. Default is :attr:`Casing.CAMEL` for
1319
+ compatibility purposes.
1320
+ include_default_values: :class:`bool`
1321
+ If ``True`` will include the default values of fields. Default is ``False``.
1322
+ E.g. an ``int32`` field will be included with a value of ``0`` if this is
1323
+ set to ``True``, otherwise this would be ignored.
1324
+
1325
+ Returns
1326
+ --------
1327
+ Dict[:class:`str`, Any]
1328
+ The python dict representation of this object.
1329
+ """
1330
+ output : Dict [str , Any ] = {}
1331
+ defaults = self ._betterproto .default_gen
1332
+ for field_name , meta in self ._betterproto .meta_by_field_name .items ():
1333
+ field_is_repeated = defaults [field_name ] is list
1334
+ value = getattr (self , field_name )
1335
+ cased_name = casing (field_name ).rstrip ("_" ) # type: ignore
1336
+ if meta .proto_type == TYPE_MESSAGE :
1337
+ if isinstance (value , datetime ):
1338
+ if (
1339
+ value != DATETIME_ZERO
1340
+ or include_default_values
1341
+ or self ._include_default_value_for_oneof (
1342
+ field_name = field_name , meta = meta
1343
+ )
1344
+ ):
1345
+ output [cased_name ] = value
1346
+ elif isinstance (value , timedelta ):
1347
+ if (
1348
+ value != timedelta (0 )
1349
+ or include_default_values
1350
+ or self ._include_default_value_for_oneof (
1351
+ field_name = field_name , meta = meta
1352
+ )
1353
+ ):
1354
+ output [cased_name ] = value
1355
+ elif meta .wraps :
1356
+ if value is not None or include_default_values :
1357
+ output [cased_name ] = value
1358
+ elif field_is_repeated :
1359
+ # Convert each item.
1360
+ value = [i .to_pydict (casing , include_default_values ) for i in value ]
1361
+ if value or include_default_values :
1362
+ output [cased_name ] = value
1363
+ elif (
1364
+ value ._serialized_on_wire
1365
+ or include_default_values
1366
+ or self ._include_default_value_for_oneof (
1367
+ field_name = field_name , meta = meta
1368
+ )
1369
+ ):
1370
+ output [cased_name ] = value .to_pydict (casing , include_default_values )
1371
+ elif meta .proto_type == TYPE_MAP :
1372
+ for k in value :
1373
+ if hasattr (value [k ], "to_pydict" ):
1374
+ value [k ] = value [k ].to_pydict (casing , include_default_values )
1375
+
1376
+ if value or include_default_values :
1377
+ output [cased_name ] = value
1378
+ elif (
1379
+ value != self ._get_field_default (field_name )
1380
+ or include_default_values
1381
+ or self ._include_default_value_for_oneof (
1382
+ field_name = field_name , meta = meta
1383
+ )
1384
+ ):
1385
+ output [cased_name ] = value
1386
+ return output
1387
+
1388
+ def from_pydict (self : T , value : Dict [str , Any ]) -> T :
1389
+ """
1390
+ Parse the key/value pairs into the current message instance. This returns the
1391
+ instance itself and is therefore assignable and chainable.
1392
+
1393
+ Parameters
1394
+ -----------
1395
+ value: Dict[:class:`str`, Any]
1396
+ The dictionary to parse from.
1397
+
1398
+ Returns
1399
+ --------
1400
+ :class:`Message`
1401
+ The initialized message.
1402
+ """
1403
+ self ._serialized_on_wire = True
1404
+ for key in value :
1405
+ field_name = safe_snake_case (key )
1406
+ meta = self ._betterproto .meta_by_field_name .get (field_name )
1407
+ if not meta :
1408
+ continue
1409
+
1410
+ if value [key ] is not None :
1411
+ if meta .proto_type == TYPE_MESSAGE :
1412
+ v = getattr (self , field_name )
1413
+ if isinstance (v , list ):
1414
+ cls = self ._betterproto .cls_by_field [field_name ]
1415
+ for item in value [key ]:
1416
+ v .append (cls ().from_pydict (item ))
1417
+ elif isinstance (v , datetime ):
1418
+ v = value [key ]
1419
+ elif isinstance (v , timedelta ):
1420
+ v = value [key ]
1421
+ elif meta .wraps :
1422
+ v = value [key ]
1423
+ else :
1424
+ # NOTE: `from_pydict` mutates the underlying message, so no
1425
+ # assignment here is necessary.
1426
+ v .from_pydict (value [key ])
1427
+ elif meta .map_types and meta .map_types [1 ] == TYPE_MESSAGE :
1428
+ v = getattr (self , field_name )
1429
+ cls = self ._betterproto .cls_by_field [f"{ field_name } .value" ]
1430
+ for k in value [key ]:
1431
+ v [k ] = cls ().from_pydict (value [key ][k ])
1432
+ else :
1433
+ v = value [key ]
1434
+
1435
+ if v is not None :
1436
+ setattr (self , field_name , v )
1437
+ return self
1438
+
1309
1439
def is_set (self , name : str ) -> bool :
1310
1440
"""
1311
1441
Check if field with the given name has been set.
0 commit comments