程序员文章、书籍推荐和程序员创业信息与资源分享平台

网站首页 > 技术文章 正文

一文掌握在 Python 中保存和加载 JSON 文件

hfteth 2025-03-17 18:22:57 技术文章 10 ℃

JSON 文件无处不在 — 从 Web API 到配置文件。让我们探索如何在 Python 中使用它们,并提供您可以立即使用的清晰示例。

基本 JSON作

将 JSON 写入文件

让我们从基础知识开始 — 将简单的字典保存到 JSON 文件:

import json

# Your data
user_data = {
    "name": "Alice Smith",
    "age": 28,
    "email": "alice@example.com",
    "interests": ["coding", "hiking", "photography"]
}

# Save to file
with open("user_data.json", "w") as file:
    json.dump(user_data, file)

这里发生了什么?
- 'open()' 在写入模式下创建一个文件 (“w”)
- 'json.dump()' 将数据直接写入文件
- 'with' 语句确保文件正确关闭
- Python 会自动处理 JSON 转换

从文件中读取 JSON

读回文件同样简单:

# Read from file
with open("user_data.json", "r") as file:
    loaded_data = json.load(file)

print(loaded_data["name"])  # Output: Alice Smith
print(loaded_data["interests"])  # Output: ['coding', 'hiking', 'photography']

使 JSON 文件可读

默认情况下,JSON 文件不进行格式化保存。让我们让它们更漂亮:

# Save with nice formatting
with open("user_data_pretty.json", "w") as file:
    json.dump(user_data, file, indent=4)

这将创建一个如下所示的文件:

{
    "name": "Alice Smith",
    "age": 28,
    "email": "alice@example.com",
    "interests": [
        "coding",
        "hiking",
        "photography"
    ]
}

'indent' 参数添加:
- 一致的间距
- 换行符
- 嵌套缩进
- 更好的人类可读性

处理复杂数据类型

Python 具有 JSON 不直接支持的数据类型。以下是处理它们的方法:

import json
from datetime import datetime

class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

# Complex data with datetime
event_data = {
    "event_name": "Conference",
    "date": datetime.now(),
    "attendees": ["Bob", "Carol", "Dave"]
}

# Save with custom encoder
with open("event_data.json", "w") as file:
    json.dump(event_data, file, cls=DateTimeEncoder)

此代码:
- 为 datetime 对象创建自定义编码器
- 将日期时间转换为 ISO 格式字符串
- 保持与标准 JSON 的兼容性

真实示例

保存应用程序设置

以下是为您的应用程序创建设置管理器的方法:

class SettingsManager:
    def __init__(self, settings_file="settings.json"):
        self.settings_file = settings_file
        self.settings = self.load_settings()
    
    def load_settings(self):
        try:
            with open(self.settings_file, "r") as file:
                return json.load(file)
        except FileNotFoundError:
            return self.get_default_settings()
    
    def save_settings(self):
        with open(self.settings_file, "w") as file:
            json.dump(self.settings, file, indent=4)
    
    def get_default_settings(self):
        return {
            "theme": "light",
            "font_size": 12,
            "notifications": True,
            "language": "en"
        }
    
    def update_setting(self, key, value):
        self.settings[key] = value
        self.save_settings()

# Usage example
settings = SettingsManager()
settings.update_setting("theme", "dark")

缓存 API 响应

以下是缓存 API 数据的实际示例:

import json
import time
from pathlib import Path

class APICache:
    def __init__(self, cache_file="api_cache.json"):
        self.cache_file = Path(cache_file)
        self.cache = self.load_cache()
    
    def load_cache(self):
        if self.cache_file.exists():
            with open(self.cache_file, "r") as file:
                return json.load(file)
        return {}
    
    def save_cache(self):
        with open(self.cache_file, "w") as file:
            json.dump(self.cache, file)
    
    def get_data(self, key, max_age=3600):
        """Get data from cache if it's fresh, return None if expired"""
        if key in self.cache:
            entry = self.cache[key]
            if time.time() - entry["timestamp"] < max_age:
                return entry["data"]
        return None
    
    def set_data(self, key, data):
        """Save data to cache with current timestamp"""
        self.cache[key] = {
            "data": data,
            "timestamp": time.time()
        }
        self.save_cache()

# Example usage
cache = APICache()
data = cache.get_data("user_profile")
if data is None:
    # Simulate API call
    data = {"name": "Alice", "last_seen": "2024-01-01"}
    cache.set_data("user_profile", data)

使用嵌套数据

在处理复杂的嵌套结构(如配置文件)时:

def save_config(config, filename="config.json"):
    """Save nested configuration with error handling"""
    try:
        with open(filename, "w") as file:
            json.dump(config, file, indent=2)
        return True
    except Exception as e:
        print(f"Error saving config: {e}")
        return False

# Complex nested configuration
app_config = {
    "database": {
        "host": "localhost",
        "port": 5432,
        "credentials": {
            "username": "admin",
            "password": "secret"
        }
    },
    "server": {
        "host": "0.0.0.0",
        "port": 8080,
        "debug": True,
        "allowed_origins": [
            "http://localhost:3000",
            "https://example.com"
        ]
    },
    "logging": {
        "level": "INFO",
        "handlers": ["console", "file"],
        "file_config": {
            "path": "/var/log/app.log",
            "max_size": 1024000,
            "backup_count": 3
        }
    }
}

save_config(app_config)

错误处理和验证

始终验证您的 JSON作:

def safe_write_json(data, filename):
    """Safely write data to a JSON file with validation"""
    try:
        # First validate by encoding to string
        json_string = json.dumps(data)
        
        # Verify it can be parsed back
        json.loads(json_string)
        
        # If we get here, the JSON is valid
        with open(filename, "w") as file:
            file.write(json_string)
        return True
    
    except json.JSONDecodeError as e:
        print(f"Invalid JSON data: {e}")
        return False
    except IOError as e:
        print(f"File error: {e}")
        return False
    except Exception as e:
        print(f"Unexpected error: {e}")
        return False

# Example usage with invalid data
invalid_data = {
    "name": "Test",
    "value": float('nan')  # This is not valid JSON
}

success = safe_write_json(invalid_data, "test.json")
print(f"Save successful: {success}")

性能提示

使用大型 JSON 文件时:

def read_json_in_chunks(filename, chunk_size=1024*8):
    """Read a large JSON file efficiently"""
    with open(filename, "r") as file:
        decoder = json.JSONDecoder()
        buffer = ""
        
        while True:
            chunk = file.read(chunk_size)
            if not chunk:
                break
            
            buffer += chunk
            try:
                while buffer:
                    result, index = decoder.raw_decode(buffer)
                    buffer = buffer[index:].lstrip()
                    yield result
            except json.JSONDecodeError:
                # Incomplete JSON data, read more
                continue

# Example usage for large files
for item in read_json_in_chunks("large_file.json"):
    process_item(item)

性能要点:
- 对大文件使用块
- 考虑使用 'ujson' 来提高速度(如果可用)
- 仅在需要时保持文件可读
- 使用适当的文件权限

常见问题和解决方案

处理 Unicode

def write_unicode_json(data, filename):
    """Write JSON with proper Unicode handling"""
    with open(filename, "w", encoding="utf-8") as file:
        json.dump(data, file, ensure_ascii=False)

# Example with Unicode data
unicode_data = {
    "name": "José",
    "city": "S~ao Paulo",
    "greeting": "你好"
}

write_unicode_json(unicode_data, "unicode_data.json")

处理特殊类型

def convert_to_serializable(obj):
    """Convert special Python types to JSON-serializable formats"""
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    elif isinstance(obj, set):
        return list(obj)
    elif isinstance(obj, bytes):
        return obj.decode('utf-8')
    raise TypeError(f"Type {type(obj)} not serializable")

# Usage example
complex_data = {
    "date": datetime.now(),
    "tags": {"python", "json", "tutorial"},
    "binary": b"Hello World"
}

with open("complex_data.json", "w") as file:
    json.dump(complex_data, file, default=convert_to_serializable)

记得:
- 始终使用正确的编码
- 显式处理特殊数据类型
- 保存前验证 JSON
- 使用适当的错误处理
- 考虑文件权限和路径

这些示例涵盖了 Python 中最常见的 JSON 文件作。选择最适合您特定需求的方法,并在用于生产之前始终使用样本数据进行测试。

最近发表
标签列表