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.

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
defaultthat gets called for any object the serializer doesn't recognize. - In this case, the function checks if the object is a
datetimeinstance and converts it to a string usingisoformat(), 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
defaultmethod checks if an object is aDecimaland converts it to afloat. - For any other data type,
super().default(obj)lets the parent encoder handle it. - You then use this class by passing it to the
clsparameter injson.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
tupleinmetadatabecomes a JSON array. - Similarly, Python's boolean values
TrueandFalseare converted to JSON's lowercasetrueandfalse.
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_2for 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_keysandindent. - Create an API that processes complex objects, like
datetimeorDecimal, 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
setto alistbefore callingjson.dumps(). - For a more automated solution, you can handle
setobjects within a custom function passed to thedefaultparameter, just as you would fordatetimeobjects.
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
ValueErrorif your dictionary contains these values, forcing you to decide how to handle them—perhaps by converting them tonullor 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=Falsein 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=Falseinjson.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=2adds 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
geometryobject holds the specific location data, like coordinates. - The corresponding
propertiesobject 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.
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.
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.



.png)