44

According to this conversion table, Python ints get written as JSON numbers when serialized using the JSON module--as I would expect and desire.

I have a dictionary with an integer key and integer value:

>>> d = {1:2}
>>> type(d.items()[0][0])
<type 'int'>
>>> type(d.items()[0][1])
<type 'int'>

When I use the json module to serialize this to a JSON string, the value is written as a number, but the key is written as a string:

>>> json.dumps(d)
'{"1": 2}'

This isn't the behavior I want, and it seems particularly broken since it breaks json.dumps/json.loads round-tripping:

>>> d == json.loads(json.dumps(d))
False

Why does this happen, and is there a way I can force the key to be written as a number?

6
  • 7
    JSON keys are always strings. Commented Jun 14, 2013 at 0:59
  • 2
    ...and JSON uses strings because they're byte-order independent. Commented Jun 14, 2013 at 1:01
  • 5
    If JSON is not strictly required, you could go for YAML using PyYAML instead : d = {1:2, 3:4}; import yaml; yaml.safe_load(yaml.safe_dump(d)) == d returns True. In general the 'inline' style of YAML looks like more flexible json (keys can be numbers, strings usually don't have to be quoted). I use safe_load here because normal yaml.load has features (construction of classes,etc) that it's hard to secure correctly; safe_load|dump restrict the set of supported input/outputs to primitive types (bool,int,float,string,list,set,dict) and so are safe to use on arbitrary inputs. Commented Jun 14, 2013 at 2:15
  • Unfortunately the round tripping does not hold with yaml.safe_dump because it converts strings of things looking like ints into ints. E.g. try it with d = {'1':'2'} Commented Oct 24, 2013 at 10:21
  • Bug for above round tripping issue: bitbucket.org/xi/pyyaml/issue/21 Commented Oct 24, 2013 at 12:28

3 Answers 3

51

The simple reason is that JSON does not allow integer keys.

object
    {}
    { members } 
members
    pair
    pair , members
pair
    string : value  # Keys *must* be strings.

As to how to get around this limitation - you will first need to ensure that the receiving implementation can handle the technically-invalid JSON. Then you can either replace all of the quote marks or use a custom serializer.

Sign up to request clarification or add additional context in comments.

1 Comment

And unlike plain javascript. all keys should be quoted.
1

If you really want to, you can check keys for being convertable to integers again using:

def pythonify(json_data):
    for key, value in json_data.iteritems():
        if isinstance(value, list):
            value = [ pythonify(item) if isinstance(item, dict) else item for item in value ]
        elif isinstance(value, dict):
            value = pythonify(value)
        try:
            newkey = int(key)
            del json_data[key]
            key = newkey
        except TypeError:
            pass
        json_data[key] = value
    return json_data

Comments

1

This function will recursively cast all string-keys to int-keys, if possible. If not possible the key-type will remain unchanged.

I adjusted JLT's example below slightly. With some of my huge nested dictionaries that code made the size of the dictionary change, ending with an exception. Anyhow, credit goes to JLT!

def pythonify(json_data):

    correctedDict = {}

    for key, value in json_data.items():
        if isinstance(value, list):
            value = [pythonify(item) if isinstance(item, dict) else item for item in value]
        elif isinstance(value, dict):
            value = pythonify(value)
        try:
            key = int(key)
        except Exception as ex:
            pass
        correctedDict[key] = value

    return correctedDict

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.