How to convert a dict to JSON in Python

Learn to convert a Python dictionary to JSON. This guide covers multiple methods, practical tips, real-world uses, and error debugging.

How to convert a dict to JSON in Python
Published on: 
Tue
Mar 3, 2026
Updated on: 
Wed
Mar 4, 2026
The Replit Team Logo Image
The Replit Team

You often need to convert a Python dict to JSON for data serialization and API communication. Python's json module makes this process simple with powerful functions for seamless data exchange.

In this article, you'll learn key techniques and practical tips for the conversion. You'll find real-world applications and common debugging advice to handle your data workflows effectively.

Using json.dumps() to convert a dictionary to JSON

import json

my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}
json_string = json.dumps(my_dict)
print(json_string)--OUTPUT--{"name": "John", "age": 30, "city": "New York"}

The json.dumps() function is the workhorse for this conversion. It takes your Python dictionary, my_dict, and serializes it into a JSON formatted string. This process is essential when you need to send data over a network or store it in a text file that other systems can easily parse.

Notice the output {"name": "John", "age": 30, "city": "New York"} uses double quotes for keys, which is standard JSON syntax. The dumps in the function name stands for "dump string," signifying that it outputs the data as a string object.

Basic JSON conversion techniques

The json.dumps() function offers more than just a basic conversion; you can use its parameters to format the output for readability and handle complex data.

Converting a dictionary to JSON with custom indentation

import json

my_dict = {'name': 'John', 'age': 30, 'city': 'New York'}
formatted_json = json.dumps(my_dict, indent=4)
print(formatted_json)--OUTPUT--{
"name": "John",
"age": 30,
"city": "New York"
}

For better readability, especially during debugging, you can use the indent parameter in json.dumps(). This "pretty-prints" the JSON output, making it much easier for a human to read. Setting indent=4 tells the function to format the string with an indentation of four spaces for each level.

  • This transforms the compact, single-line string into a structured, multi-line block.
  • It’s perfect for development, logging, or configuration files where you might need to inspect the data manually.

Handling non-serializable objects with default parameter

import json
from datetime import datetime

def convert_datetime(obj):
if isinstance(obj, datetime):
return obj.isoformat()
raise TypeError("Type not serializable")

my_dict = {'event': 'meeting', 'timestamp': datetime(2023, 5, 15, 14, 30)}
json_string = json.dumps(my_dict, default=convert_datetime)
print(json_string)--OUTPUT--{"event": "meeting", "timestamp": "2023-05-15T14:30:00"}

Sometimes your dictionary contains objects that aren't standard JSON types, like a datetime object. Trying to serialize them directly will cause a TypeError. The default parameter in json.dumps() is your solution for this, letting you define a custom conversion rule.

  • You pass a function to default that gets called for any object the serializer doesn't recognize.
  • In this case, the function checks if the object is a datetime instance and converts it to a string using isoformat(), which is JSON-compatible.

Sorting keys in JSON output with sort_keys

import json

unsorted_dict = {'c': 3, 'a': 1, 'b': 2, 'd': {'z': 26, 'y': 25, 'x': 24}}
sorted_json = json.dumps(unsorted_dict, sort_keys=True, indent=2)
print(sorted_json)--OUTPUT--{
"a": 1,
"b": 2,
"c": 3,
"d": {
"x": 24,
"y": 25,
"z": 26
}
}

When you need consistent output, the sort_keys parameter is your go-to. By setting it to True, you instruct json.dumps() to arrange the dictionary keys alphabetically before creating the JSON string. This is incredibly helpful for a few reasons:

  • It ensures that serializing the same dictionary always produces the exact same string, which is great for tasks like caching or writing unit tests.
  • The sorting also applies recursively, meaning keys within any nested dictionaries are sorted too.

Advanced JSON conversion techniques

While the default parameter is useful, you can gain even more control and performance by creating custom encoders and leveraging specialized libraries for complex serialization tasks.

Working with custom JSON encoders

import json
from decimal import Decimal

class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, Decimal):
return float(obj)
return super().default(obj)

my_dict = {'price': Decimal('19.99'), 'qty': 3}
json_string = json.dumps(my_dict, cls=CustomEncoder)
print(json_string)--OUTPUT--{"price": 19.99, "qty": 3}

For more complex serialization, you can create a custom encoder by subclassing json.JSONEncoder. This gives you a reusable way to handle specific data types across your application. In the example, the CustomEncoder class overrides the default method to handle Decimal objects, which aren't natively serializable.

  • The default method checks if an object is a Decimal and converts it to a float.
  • For any other data type, super().default(obj) lets the parent encoder handle it.
  • You then use this class by passing it to the cls parameter in json.dumps().

This approach is cleaner than using a default function when you have multiple custom types.

Converting nested dictionaries with complex data types

import json

complex_dict = {
'user': {'name': 'Alice', 'roles': ['admin', 'user']},
'permissions': {'read': True, 'write': True, 'delete': False},
'metadata': {'tags': ('important', 'urgent'), 'priority': 1}
}
json_string = json.dumps(complex_dict, indent=2)
print(json_string)--OUTPUT--{
"user": {
"name": "Alice",
"roles": [
"admin",
"user"
]
},
"permissions": {
"read": true,
"write": true,
"delete": false
},
"metadata": {
"tags": [
"important",
"urgent"
],
"priority": 1
}
}

The json.dumps() function seamlessly handles nested dictionaries. It recursively processes inner dictionaries and lists, converting the entire structure into a valid JSON string. This is powerful because you don't need to write custom logic for each level of nesting.

  • Notice how Python data types are automatically mapped to their JSON counterparts. For example, the tuple in metadata becomes a JSON array.
  • Similarly, Python's boolean values True and False are converted to JSON's lowercase true and false.

Using orjson for high-performance JSON serialization

import orjson

large_dict = {str(i): i for i in range(1000, 1005)}
json_bytes = orjson.dumps(large_dict, option=orjson.OPT_INDENT_2)
print(json_bytes.decode('utf-8'))--OUTPUT--{
"1000": 1000,
"1001": 1001,
"1002": 1002,
"1003": 1003,
"1004": 1004
}

When performance is critical, especially with large datasets, the orjson library is a powerful alternative to Python's built-in json module. It's designed for speed and can significantly accelerate your data serialization tasks.

  • A key difference is that orjson.dumps() returns a byte string, not a text string.
  • You'll need to call .decode('utf-8') on the output to get a human-readable string.
  • It also provides formatting options like orjson.OPT_INDENT_2 for pretty-printing.

Move faster with Replit

Replit is an AI-powered development platform that transforms natural language into working applications. You can describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.

For the JSON conversion techniques we've explored, Replit Agent can turn them into production-ready tools. You can use it to:

  • Build a configuration file generator that takes user settings and outputs a sorted, human-readable JSON file using sort_keys and indent.
  • Create an API that processes complex objects, like datetime or Decimal, and serializes them into a clean JSON response with a custom encoder.
  • Deploy a data pipeline that ingests nested Python dictionaries and exports them as standardized JSON for other services to consume.

Try describing your app idea, and Replit Agent will write the code, test it, and fix issues automatically, all from your browser.

Common errors and challenges

Even with powerful tools, you might hit snags with non-serializable types, special numeric values, or character encoding during JSON conversion.

Troubleshooting errors with non-serializable types like set

You'll get a TypeError if you try to serialize a Python set because it has no direct equivalent in JSON. The JSON standard only defines objects (dictionaries), arrays (lists), strings, numbers, booleans, and null.

  • The simplest fix is to convert the set to a list before calling json.dumps().
  • For a more automated solution, you can handle set objects within a custom function passed to the default parameter, just as you would for datetime objects.

Handling NaN and Infinity values in JSON serialization

Python's floating-point numbers can represent special values like NaN, Infinity, and -Infinity, but these aren't part of the official JSON specification. While json.dumps() will convert them by default, many strict JSON parsers in other languages will reject the output.

  • To enforce compliance, you can set allow_nan=False.
  • This will raise a ValueError if your dictionary contains these values, forcing you to decide how to handle them—perhaps by converting them to null or a specific string.

Resolving encoding errors with non-ASCII characters

By default, json.dumps() escapes all non-ASCII characters, which can lead to a UnicodeEncodeError or create output like \u2764 instead of a heart emoji ❤️. This happens because the function prioritizes compatibility with older systems.

  • To preserve these characters, set ensure_ascii=False in your function call.
  • This tells the serializer to output the characters as they are, producing a more readable and modern UTF-8 encoded string.

Troubleshooting errors with non-serializable types like set

Python's set is useful for storing unique items, but it's not a valid JSON type. When you pass a dictionary containing a set to json.dumps(), you'll trigger a TypeError. The following code demonstrates exactly what happens in this scenario.

import json

user_data = {
'name': 'Alice',
'skills': {'Python', 'JavaScript', 'SQL'} # Using a set
}
json_string = json.dumps(user_data)
print(json_string)

The json.dumps() function fails because the skills key holds a set, which isn't a serializable JSON type. This mismatch triggers a TypeError. The following code shows how to fix this and successfully serialize the dictionary.

import json

user_data = {
'name': 'Alice',
'skills': list({'Python', 'JavaScript', 'SQL'}) # Convert set to list
}
json_string = json.dumps(user_data)
print(json_string)

The fix is to convert the set to a list before serialization. This works because JSON's array type maps directly to a Python list, allowing json.dumps() to process the data without a TypeError.

  • Watch for this issue when you're using sets for their uniqueness but need to serialize the data for an API response or file storage.

Handling NaN and Infinity values in JSON serialization

Python's special numeric values like NaN and Infinity aren't standard in JSON. By default, json.dumps() converts them, but this can create non-compliant output that other systems might reject. The following code shows what this default conversion looks like in practice.

import json
import math

data = {
'normal_value': 42,
'special_value': float('inf'), # Infinity
'error_value': float('nan') # NaN
}

json_string = json.dumps(data)
print(json_string)

The output string contains the literal values Infinity and NaN because json.dumps() converts Python's special floats by default. This non-standard output can break other tools. The following code shows how to prevent this and ensure compliance.

import json
import math

def serialize_special_floats(obj):
if isinstance(obj, float):
if math.isnan(obj):
return "NaN"
if math.isinf(obj):
return "Infinity" if obj > 0 else "-Infinity"
return obj

data = {
'normal_value': 42,
'special_value': float('inf'),
'error_value': float('nan')
}

json_string = json.dumps(data, default=serialize_special_floats)
print(json_string)

The solution is to pass a custom function to the default parameter of json.dumps(). This function intercepts special float values, using the math module to check for NaN or Infinity, and converts them into strings. This approach gives you explicit control over the output, ensuring your JSON is compatible with other systems. You'll want to watch for this when working with scientific or statistical data where these values can occur.

Resolving encoding errors with non-ASCII characters

You'll run into encoding issues when data includes non-ASCII characters like 'é' or 'ç'. By default, json.dumps() converts them into escape sequences, which can cause problems when writing to files. The following code demonstrates this default behavior in action.

import json

user_info = {
'name': 'José',
'location': 'São Paulo'
}

with open('user.json', 'w') as f:
f.write(json.dumps(user_info))

The open() call is the issue. Its default encoding might not handle the escaped Unicode from json.dumps(), causing a UnicodeEncodeError. The next example shows how to write the file correctly to avoid this problem.

import json

user_info = {
'name': 'José',
'location': 'São Paulo'
}

with open('user.json', 'w', encoding='utf-8') as f:
f.write(json.dumps(user_info, ensure_ascii=False))

The solution combines two adjustments to handle non-ASCII characters correctly.

  • Set ensure_ascii=False in json.dumps() to output characters like 'é' directly instead of as escape codes.
  • Specify encoding='utf-8' when opening the file to ensure it can handle the full range of Unicode characters.

This combination prevents encoding errors. You'll want to use this approach whenever your data might contain international text, names, or symbols.

Real-world applications

Beyond troubleshooting, these conversion skills are essential for real-world tasks like creating API responses and generating data for mapping applications.

Creating a REST API response with json.dumps()

In web development, you'll often use json.dumps() to structure your application's data into a clean, standardized JSON response for a REST API.

import json

# Simulate creating a web API response
def generate_api_response():
users = [
{'id': 1, 'name': 'Alice', 'email': 'alice@example.com'},
{'id': 2, 'name': 'Bob', 'email': 'bob@example.com'}
]
response = json.dumps({'status': 'success', 'data': users}, indent=2)
return response

api_response = generate_api_response()
print(api_response)

This example shows how to package data for a web API. The generate_api_response function assembles a list of user dictionaries and places it inside a top-level dictionary that also includes a status message.

  • The json.dumps() function serializes this nested Python object into a single JSON string.
  • Using indent=2 adds whitespace, which organizes the final string into a hierarchical layout.

This process turns your Python data into a universally accepted format ready for transmission over the web.

Generating GeoJSON for mapping applications

You can also use json.dumps() to format geographic data into the GeoJSON standard, which is essential for creating interactive maps.

import json

# Geographic data for map visualization
geo_data = {
'type': 'FeatureCollection',
'features': [
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-122.4194, 37.7749]
},
'properties': {
'name': 'San Francisco',
'population': 874961
}
},
{
'type': 'Feature',
'geometry': {
'type': 'Point',
'coordinates': [-74.0060, 40.7128]
},
'properties': {
'name': 'New York',
'population': 8336817
}
}
]
}

geo_json = json.dumps(geo_data, indent=2)
print(geo_json)

This code builds a Python dictionary, geo_data, that adheres to the GeoJSON specification. It organizes geographic information into a FeatureCollection containing a list of individual features. The json.dumps() function then serializes this complex, nested dictionary into a properly formatted JSON string.

  • Each feature's geometry object holds the specific location data, like coordinates.
  • The corresponding properties object stores descriptive metadata, such as the city's name and population.

Get started with Replit

Turn what you've learned into a real tool. Tell Replit Agent to “build an API that converts dictionaries with datetime objects to JSON” or “create a config file generator that outputs sorted, indented JSON.”

The agent writes the code, tests for errors, and deploys your app, handling the entire development lifecycle from a single prompt. Start building with Replit.

Get started free

Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

Get started for free

Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.