Converting identifier naming between camelCase and underscores during JSON serialization/deserialization

One way to do it using regular expressions,

import re

camel_pat = re.compile(r'([A-Z])')
under_pat = re.compile(r'_([a-z])')

def camel_to_underscore(name):
    return camel_pat.sub(lambda x: '_' + x.group(1).lower(), name)

def underscore_to_camel(name):
    return under_pat.sub(lambda x: x.group(1).upper(), name)

And,

>>> camel_to_underscore('camelCaseNames')
'camel_case_names'
>>> underscore_to_camel('names_with_underscores')
'namesWithUnderscores'

Note: You have to use a function (lambda expression here) for accomplishing the case change but that seems pretty straightforward.

EDIT:

If you truly wanted to intercept and adjust json objects between Python and Javascript you would have to rewrite functionality of the json module. But I think that is much more trouble than it's worth. Instead something like this would be equivalent and not be too much of a hit performance-wise.

To convert each key in a dict representing your json object, you can do something like this,

def convert_json(d, convert):
    new_d = {}
    for k, v in d.iteritems():
        new_d[convert(k)] = convert_json(v,convert) if isinstance(v,dict) else v
    return new_d

You only need to provide which function to apply,

>>> json_obj = {'nomNom': {'fooNom': 2, 'camelFoo': 3}, 'camelCase': {'caseFoo': 4, 'barBar': {'fooFoo': 44}}}
>>> convert_json(json_obj, camel_to_underscore)
{'nom_nom': {'foo_nom': 2, 'camel_foo': 3}, 'camel_case': {'case_foo': 4, 'bar_bar': {'foo_foo': 44}}}

You can wrap all of this logic in new load and dump functions,

import json

def convert_load(*args, **kwargs):
    json_obj = json.load(*args, **kwargs)
    return convert_json(json_obj, camel_to_underscore)

def convert_dump(*args, **kwargs):
    args = (convert_json(args[0], underscore_to_camel),) + args[1:]
    json.dump(*args, **kwargs)

And use then just as you would json.load and json.dump.


Jared's answer does not take into account the possibility of arrays with objects in a json object structure.

The solution requires three functions to recursively handle the arrays.

For converting from CamelCase to underscores_with_spaces:

def convert(s):
    a = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))')
    return a.sub(r'_\1', s).lower()

For the json object

def convertJSON(j):
    out = {}
    for k in j:
        newK = convert(k)
        if isinstance(j[k],dict):
            out[newK] = convertJSON(j[k])
        elif isinstance(j[k],list):
            out[newK] = convertArray(j[k])
        else:
            out[newK] = j[k]
    return out

For arrays within the json object:

def convertArray(a):
    newArr = []
    for i in a:
        if isinstance(i,list):
            newArr.append(convertArray(i))
        elif isinstance(i, dict):
            newArr.append(convertJSON(i))
        else:
            newArr.append(i)
    return newArr

Usage:

convertJSON({
    "someObject": [
        {
            "anotherObject": "CamelCaseValue"
        },
        {
            "anotherObject": "AnotherCamelCaseValue"
        }
    ]
})

Yields:

{
    'some_object': [
        {
            'another_object': 'CamelCaseValue'
        },
        {
            'another_object': 'AnotherCamelCaseValue'
        }
    ]
}

For future googlers, the humps package can do this for you.

import humps
humps.decamelize({'outerKey': {'innerKey': 'value'}})
# {'outer_key': {'inner_key': 'value'}}

Tags:

Python

Json