Python · dict · nested JSON

Convert Python dict keys to camelCase recursively

A string helper is not enough for nested dictionaries and lists. Convert keys recursively at the data boundary, preserve values, and fail on collisions.

By Camel Case Converter Editorial TeamPublished Sep 12, 2026Last reviewed

Recursively convert dict and list keys

from typing import Any

def snake_to_camel(name: str) -> str:
    parts = [part for part in name.split("_") if part]
    if not parts: return name
    head, *tail = parts
    return head.lower() + "".join(part[:1].upper() + part[1:].lower() for part in tail)

def keys_to_camel(value: Any) -> Any:
    if isinstance(value, list): return [keys_to_camel(item) for item in value]
    if not isinstance(value, dict): return value
    output: dict[Any, Any] = {}
    for key, child in value.items():
        next_key = snake_to_camel(key) if isinstance(key, str) else key
        if next_key in output: raise ValueError(f"Key collision: {next_key}")
        output[next_key] = keys_to_camel(child)
    return output

Handle lists before dictionaries, return scalar values unchanged, and rebuild each dict with converted string keys.

Use the converter after parsing JSON

import json
snake_payload = json.loads(raw_json)
camel_payload = keys_to_camel(snake_payload)
print(json.dumps(camel_payload, indent=2))

json.loads produces ordinary dict and list structures, making the naming boundary easy to locate and test.

Preserve semantic underscore or dollar prefixes when needed

def snake_to_camel_preserving_prefix(name: str) -> str:
    prefix_length = len(name) - len(name.lstrip("_$"))
    prefix = name[:prefix_length]
    body = name[prefix_length:]
    return prefix + snake_to_camel(body)

For keys such as _id, __typename, or $schema_version, separate meaningful prefixes before converting the remaining identifier.

Production considerations

  • Decide whether repeated underscores and empty tokens should be normalized or preserved.
  • A collision such as user_id and userId becoming one output key should raise an error.
  • Use excluded keys or stop paths when only part of a payload belongs to the naming convention.
  • Custom classes, dataclasses, ORM models, and serializers may provide a better boundary than recursive dict conversion.

Test the JSON boundary online

Use the browser tool to test nested objects, arrays, excluded keys, stop paths, semantic prefixes, collision handling, and a key-only diff before you copy the same convention into an application.

Drop a .json file here or choose one from your device

Files are read only in your browser. Maximum file size: 5 MB.

Converted JSON
{
  "userProfile": {
    "firstName": "Ada",
    "createdAt": "2026-09-12"
  }
}

Parsing, file reading, formatting, and key conversion run locally in your browser. JSON input is not sent to a conversion server.