danielgtaylor / danielgtaylor/python-betterproto
Integer keys in maps are not parsed correctly from JSON
- Dominant language
- Python
- Stars
- 1.8k
- Forks
- 234
- PR merge metrics
- No merged PRs in 30d
Description
JSON requires that keys be represented as strings, however proto allows map keys to be any integral or string type. So when specifying a message with a JSON representation, integer keys must be represented as strings. These are not picked up by the `.from_json()` method, which just runs `json.loads(value)`.
Example:
The proto message
```
message MapTest {
map string_map = 1;
map int_map = 2;
}
```
with JSON example:
```json
{
"string_map": {
"hello": 2.3,
"there": 5.6
},
"int_map": {
"1": 7.9,
"2": 8.3
}
}
```
Would be parsed by better proto as
`MapTest(string_map={'hello': 2.3, 'there': 5.6}, int_map={'1': 7.9, '2': 8.3})`
To overcome this we just need to add a custom JSON decoder to the from_json method which looks for any keys which are integers.
An example implementation, adapted from this [link](https://stackoverflow.com/questions/45068797/how-to-convert-string-int-json-into-real-int-with-json-loads) would be:
```
class Decoder(json.JSONDecoder):
def decode(self, s):
result = super().decode(s) # result = super(Decoder, self).decode(s) for Python 2.x
return self._decode(result)
def _decode(self, o):
if isinstance(o, str):# or isinstance(o, unicode):
try:
return int(o)
except ValueError:
return o
elif isinstance(o, dict):
try:
return {int(k): self._decode(v) for k, v in o.items()}
except ValueError:
return {k: self._decode(v) for k, v in o.items()}
elif isinstance(o, list):
return [self._decode(v) for v in o]
else:
return o
```
We'd then run `json.loads(value, cls=Decoder)`
Contributor guide
Assessment
This issue has not been assessed yet.