Diff JSON and JSON-like structures in Python with multiple syntax support and bidirectional patching
—
JSON and YAML loading, dumping, and unified serialization interface supporting both string and file operations. The serialization system provides flexible data format handling with consistent APIs across different formats.
Core JSON loading and dumping functionality with configurable options for parsing and formatting.
class JsonLoader:
"""Load JSON data from file-like objects or strings."""
def __init__(self, **kwargs):
"""
Initialize JSON loader with options.
Parameters:
- **kwargs: Arguments passed to json.loads() and json.load()
- parse_float: callable to parse float values
- parse_int: callable to parse integer values
- parse_constant: callable to parse constants (null, true, false)
- object_hook: callable to transform decoded objects
- object_pairs_hook: callable to transform decoded object pairs
"""
def __call__(self, src):
"""
Parse and return JSON data.
Parameters:
- src: str or file-like object containing JSON data
Returns:
dict: Parsed JSON data
Raises:
- JSONDecodeError: If JSON is malformed
"""
class JsonDumper:
"""Write JSON data to strings or file-like objects."""
def __init__(self, **kwargs):
"""
Initialize JSON dumper with formatting options.
Parameters:
- **kwargs: Arguments passed to json.dumps() and json.dump()
- indent: int or str for pretty-printing indentation
- separators: tuple of (item_separator, key_separator)
- sort_keys: bool to sort dictionary keys
- ensure_ascii: bool to escape non-ASCII characters
- skipkeys: bool to skip non-serializable keys
"""
def __call__(self, obj, dest=None):
"""
Serialize object to JSON.
Parameters:
- obj: Object to serialize
- dest: Optional file-like object to write to
Returns:
str: JSON string if dest is None, otherwise None
"""Usage Examples:
from jsondiff import JsonLoader, JsonDumper
import io
# Basic JSON loading
loader = JsonLoader()
data = loader('{"name": "Alice", "age": 30}')
print(data) # {'name': 'Alice', 'age': 30}
# Custom parsing options
custom_loader = JsonLoader(
parse_float=lambda x: round(float(x), 2), # Round floats
parse_int=lambda x: int(x) if int(x) < 1000 else str(x) # Large ints as strings
)
data = custom_loader('{"price": 123.456789, "count": 50000}')
print(data) # {'price': 123.46, 'count': '50000'}
# File loading
with open('data.json', 'r') as f:
data = loader(f)
# JSON dumping with formatting
dumper = JsonDumper(indent=2, sort_keys=True)
json_str = dumper({'name': 'Bob', 'age': 25})
print(json_str)
# {
# "age": 25,
# "name": "Bob"
# }
# Compact dumping
compact_dumper = JsonDumper(separators=(',', ':'))
compact_str = compact_dumper({'a': 1, 'b': 2})
print(compact_str) # {"a":1,"b":2}
# File dumping
with open('output.json', 'w') as f:
dumper({'result': 'success'}, f)YAML loading and dumping functionality for human-readable configuration files and data interchange.
class YamlLoader:
"""Load YAML data from file-like objects or strings."""
def __call__(self, src):
"""
Parse and return YAML data using safe loading.
Parameters:
- src: str or file-like object containing YAML data
Returns:
dict: Parsed YAML data
Raises:
- YAMLError: If YAML is malformed
"""
class YamlDumper:
"""Write YAML data to strings or file-like objects."""
def __init__(self, **kwargs):
"""
Initialize YAML dumper with formatting options.
Parameters:
- **kwargs: Arguments passed to yaml.dump()
- indent: int for indentation level
- default_flow_style: bool for flow vs block style
- width: int for line width
- allow_unicode: bool to allow unicode characters
- sort_keys: bool to sort dictionary keys
"""
def __call__(self, obj, dest=None):
"""
Serialize object to YAML.
Parameters:
- obj: Object to serialize
- dest: Optional file-like object to write to
Returns:
str: YAML string if dest is None, otherwise None
"""Usage Examples:
from jsondiff import YamlLoader, YamlDumper
# Basic YAML loading
loader = YamlLoader()
yaml_data = """
name: Alice
age: 30
skills:
- Python
- Django
"""
data = loader(yaml_data)
print(data) # {'name': 'Alice', 'age': 30, 'skills': ['Python', 'Django']}
# File loading
with open('config.yaml', 'r') as f:
config = loader(f)
# YAML dumping with formatting
dumper = YamlDumper(indent=2, default_flow_style=False)
data = {'database': {'host': 'localhost', 'port': 5432}, 'debug': True}
yaml_str = dumper(data)
print(yaml_str)
# database:
# host: localhost
# port: 5432
# debug: true
# Compact flow style
flow_dumper = YamlDumper(default_flow_style=True)
compact_yaml = flow_dumper({'a': 1, 'b': [2, 3]})
print(compact_yaml) # {a: 1, b: [2, 3]}
# File dumping
with open('output.yaml', 'w') as f:
dumper(data, f)Unified interface for both JSON and YAML serialization with consistent error handling and format detection.
class Serializer:
"""
Unified serialization helper for JSON and YAML formats.
"""
def __init__(self, file_format, indent):
"""
Initialize serializer for specific format.
Parameters:
- file_format: str, 'json' or 'yaml'
- indent: int, indentation level for output formatting
Raises:
- ValueError: If file_format is not supported
"""
def deserialize_file(self, src):
"""
Deserialize file or string in the configured format.
Parameters:
- src: str or file-like object containing serialized data
Returns:
dict: Parsed data
Raises:
- ValueError: If file contains invalid format data
"""
def serialize_data(self, obj, stream):
"""
Serialize object and write to stream.
Parameters:
- obj: Object to serialize
- stream: Writable stream to output to
"""Usage Examples:
from jsondiff import Serializer
import sys
# JSON serializer
json_serializer = Serializer('json', indent=2)
# Load JSON data
json_data = '{"users": [{"name": "Alice"}, {"name": "Bob"}]}'
parsed = json_serializer.deserialize_file(json_data)
print(parsed) # {'users': [{'name': 'Alice'}, {'name': 'Bob'}]}
# Write to stdout
json_serializer.serialize_data(parsed, sys.stdout)
# YAML serializer
yaml_serializer = Serializer('yaml', indent=2)
# Load YAML data
yaml_data = """
users:
- name: Alice
- name: Bob
"""
parsed = yaml_serializer.deserialize_file(yaml_data)
# Write to file
with open('output.yaml', 'w') as f:
yaml_serializer.serialize_data(parsed, f)
# File operations
with open('input.json', 'r') as input_file:
data = json_serializer.deserialize_file(input_file)
with open('output.json', 'w') as output_file:
json_serializer.serialize_data(data, output_file)
# Error handling
try:
invalid_serializer = Serializer('xml', 2)
except ValueError as e:
print(f"Unsupported format: {e}")
try:
bad_data = json_serializer.deserialize_file('{"invalid": json}')
except ValueError as e:
print(f"Parse error: {e}")Pre-configured loader and dumper instances for common use cases.
# Default instances available for import
default_loader: JsonLoader # Default JSON loader with no special options
default_dumper: JsonDumper # Default JSON dumper with no special formattingUsage Examples:
from jsondiff import default_loader, default_dumper
# Use default instances directly
data = default_loader('{"key": "value"}')
json_string = default_dumper(data)
# Equivalent to
from jsondiff import JsonLoader, JsonDumper
loader = JsonLoader()
dumper = JsonDumper()The serialization classes integrate seamlessly with JsonDiffer for automatic loading and dumping.
Usage Examples:
from jsondiff import JsonDiffer, JsonLoader, JsonDumper, YamlLoader, YamlDumper
# Auto-loading JSON strings
json_differ = JsonDiffer(load=True, loader=JsonLoader())
result = json_differ.diff('{"a": 1}', '{"a": 2}')
# Auto-dumping with custom formatting
pretty_dumper = JsonDumper(indent=4, sort_keys=True)
pretty_differ = JsonDiffer(dump=True, dumper=pretty_dumper)
json_result = pretty_differ.diff({'b': 1}, {'b': 2}) # Returns formatted JSON
# YAML workflow
yaml_differ = JsonDiffer(
load=True,
dump=True,
loader=YamlLoader(),
dumper=YamlDumper(default_flow_style=False)
)
yaml1 = "name: Alice\nage: 30"
yaml2 = "name: Alice\nage: 31\ncity: NYC"
yaml_diff = yaml_differ.diff(yaml1, yaml2) # Returns YAML string
# Mixed format processing
json_loader = JsonLoader()
yaml_dumper = YamlDumper(indent=2)
mixed_differ = JsonDiffer(load=True, dump=True, loader=json_loader, dumper=yaml_dumper)
# Load JSON, output YAML
result = mixed_differ.diff('{"a": 1}', '{"a": 2}') # JSON in, YAML outRobust error handling for malformed data and unsupported formats.
from jsondiff import Serializer, JsonLoader, YamlLoader
from json import JSONDecodeError
from yaml import YaMLError
# Format validation
try:
bad_serializer = Serializer('unsupported_format', 2)
except ValueError as e:
print(f"Format error: {e}")
# JSON parsing errors
json_loader = JsonLoader()
try:
data = json_loader('{"malformed": json}')
except JSONDecodeError as e:
print(f"JSON error: {e}")
# YAML parsing errors
yaml_loader = YamlLoader()
try:
data = yaml_loader('malformed: yaml: content:')
except YAMLError as e:
print(f"YAML error: {e}")
# File handling errors
serializer = Serializer('json', 2)
try:
with open('nonexistent.json', 'r') as f:
data = serializer.deserialize_file(f)
except FileNotFoundError as e:
print(f"File error: {e}")Install with Tessl CLI
npx tessl i tessl/pypi-jsondiff