danielgtaylor / danielgtaylor/python-betterproto

Integer keys in maps are not parsed correctly from JSON

Open
#235 5 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.