CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-jsondiff

Diff JSON and JSON-like structures in Python with multiple syntax support and bidirectional patching

Pending
Overview
Eval results
Files

jsondiff-class.mddocs/

JsonDiffer Configuration

The main class providing configurable diff computation with options for syntax, serialization, marshaling, and path exclusion. JsonDiffer serves as the central orchestrator for all diff operations and provides fine-grained control over the diff process.

Capabilities

JsonDiffer Class

Central class managing diff computation with comprehensive configuration options for different use cases and output requirements.

class JsonDiffer:
    """
    A class for computing differences between two JSON structures and applying patches.
    """
    def __init__(self, syntax='compact', load=False, dump=False, marshal=False,
                 loader=None, dumper=None, escape_str='$'):
        """
        Initialize JsonDiffer with specified options.
        
        Parameters:
        - syntax: str or syntax class, diff output format (default: 'compact')
          - 'compact': Minimal output size
          - 'explicit': Human-readable with clear operation labels
          - 'symmetric': Bidirectional with original and modified values
          - 'rightonly': Focus on final state values
        - load: bool, automatically load JSON from strings or files (default: False)
        - dump: bool, automatically dump output to JSON strings or files (default: False) 
        - marshal: bool, marshal diffs to handle special characters (default: False)
        - loader: callable, custom function for loading JSON data (default: built-in JsonLoader)
        - dumper: callable, custom function for dumping JSON data (default: built-in JsonDumper)
        - escape_str: str, string used to escape special characters (default: '$')
        """
        
    def diff(self, a, b, fp=None, exclude_paths=None):
        """
        Compute difference between two JSON structures.
        
        Parameters:
        - a: Original JSON structure
        - b: Modified JSON structure
        - fp: Optional file pointer to dump diff to
        - exclude_paths: list of string paths to exclude from diff
        
        Returns:
        dict: Computed diff structure
        """
        
    def similarity(self, a, b):
        """
        Calculate similarity score between two JSON structures.
        
        Parameters:
        - a: First JSON structure
        - b: Second JSON structure
        
        Returns:
        float: Similarity score between 0.0 and 1.0
        """
        
    def patch(self, a, d, fp=None):
        """
        Apply diff to JSON structure to produce modified structure.
        
        Parameters:
        - a: Original JSON structure to patch
        - d: Diff to apply
        - fp: Optional file pointer to dump result to
        
        Returns:
        JSON structure: Patched structure
        """
        
    def unpatch(self, b, d, fp=None):
        """
        Reverse diff on JSON structure to produce original structure.
        Available only with symmetric syntax.
        
        Parameters:
        - b: Modified JSON structure
        - d: Diff that was applied
        - fp: Optional file pointer to dump result to
        
        Returns:
        JSON structure: Original structure before diff
        """
        
    def marshal(self, d):
        """
        Convert structure to marshaled form with escaped special characters.
        
        Parameters:
        - d: Structure to marshal
        
        Returns:
        Marshaled structure with escaped symbols
        """
        
    def unmarshal(self, d):
        """
        Convert marshaled structure back to original form.
        
        Parameters:
        - d: Marshaled structure to unmarshal
        
        Returns:
        Original structure with unescaped symbols
        """

Configuration Options

The JsonDiffer class provides extensive configuration through its Options inner class and constructor parameters.

class JsonDiffer:
    class Options:
        """Configuration options container for JsonDiffer."""
        pass

Usage Examples:

from jsondiff import JsonDiffer, JsonLoader, JsonDumper

# Basic configuration with different syntaxes
compact_differ = JsonDiffer(syntax='compact')
explicit_differ = JsonDiffer(syntax='explicit')
symmetric_differ = JsonDiffer(syntax='symmetric')

# Auto-loading from JSON strings
auto_loader = JsonDiffer(load=True)
result = auto_loader.diff('{"a": 1}', '{"a": 2, "b": 3}')

# Auto-dumping to JSON strings
auto_dumper = JsonDiffer(dump=True)
json_result = auto_dumper.diff({'a': 1}, {'a': 2})  # Returns JSON string

# Marshaling for safe serialization
marshal_differ = JsonDiffer(marshal=True)
result = marshal_differ.diff({'$delete': 'value'}, {'$delete': 'new_value'})
# Symbols are escaped: {'$$delete': 'new_value'}

# Custom loaders and dumpers
custom_loader = JsonLoader(parse_float=float, parse_int=int)
custom_dumper = JsonDumper(indent=2, sort_keys=True)
custom_differ = JsonDiffer(loader=custom_loader, dumper=custom_dumper)

# Custom escape string
custom_escape = JsonDiffer(escape_str='#')
# Symbols will be escaped with '#' instead of '$'

Path Exclusion

Exclude specific JSON paths from diff computation, useful for ignoring timestamps, IDs, or other dynamic fields.

Usage Examples:

from jsondiff import JsonDiffer

differ = JsonDiffer()

# Single path exclusion
data1 = {'user': {'name': 'John', 'last_login': '2023-01-01', 'id': 123}}
data2 = {'user': {'name': 'John', 'last_login': '2023-01-02', 'id': 123}}

result = differ.diff(data1, data2, exclude_paths=['user.last_login'])
# Result: {} (no differences after excluding timestamp)

# Multiple path exclusion
data1 = {
    'users': [
        {'name': 'Alice', 'id': 1, 'created': '2023-01-01'},
        {'name': 'Bob', 'id': 2, 'created': '2023-01-02'}
    ],
    'metadata': {'version': '1.0', 'timestamp': '2023-01-01T10:00:00'}
}

data2 = {
    'users': [
        {'name': 'Alice', 'id': 1, 'created': '2023-01-01'},
        {'name': 'Bob', 'id': 2, 'created': '2023-01-02'},
        {'name': 'Charlie', 'id': 3, 'created': '2023-01-03'}
    ],
    'metadata': {'version': '1.1', 'timestamp': '2023-01-03T15:30:00'}
}

result = differ.diff(data1, data2, exclude_paths=[
    'metadata.timestamp',
    'users.created'  # Note: This excludes the path pattern, not array elements
])
# Result focuses on structural changes, ignoring timestamps

Advanced Configuration

Configure JsonDiffer for specialized use cases with custom serialization and processing options.

Usage Examples:

from jsondiff import JsonDiffer, YamlLoader, YamlDumper
import json

# YAML processing configuration
yaml_differ = JsonDiffer(
    load=True,
    dump=True,
    loader=YamlLoader(),
    dumper=YamlDumper(default_flow_style=False)
)

# File-based operations
with open('config1.yaml', 'r') as f1, open('config2.yaml', 'r') as f2:
    with open('diff.yaml', 'w') as output:
        yaml_differ.diff(f1, f2, fp=output)

# High-precision numeric handling
class HighPrecisionLoader:
    def __call__(self, src):
        return json.loads(src, parse_float=lambda x: x)  # Keep as string

precision_differ = JsonDiffer(loader=HighPrecisionLoader())

# Streaming large diffs
class StreamingDumper:
    def __call__(self, obj, dest=None):
        if dest:
            json.dump(obj, dest, separators=(',', ':'))  # Compact output
        else:
            return json.dumps(obj, separators=(',', ':'))

streaming_differ = JsonDiffer(dump=True, dumper=StreamingDumper())

# Combining multiple options
production_differ = JsonDiffer(
    syntax='compact',      # Minimal storage
    marshal=True,          # Safe for serialization
    load=True,            # Accept JSON strings
    dump=True,            # Return JSON strings
    escape_str='@'        # Custom escape character
)

Marshaling and Unmarshaling

Handle special characters and symbols safely for serialization and storage.

Usage Examples:

from jsondiff import JsonDiffer, delete, insert

differ = JsonDiffer(marshal=True)

# Data with symbol-like keys
original = {'$delete': 'some_value', 'normal_key': 'data'}
modified = {'$delete': 'updated_value', 'normal_key': 'data', '$insert': 'new'}

# Marshal handles symbol conflicts
result = differ.diff(original, modified)
# Symbols are properly escaped to avoid conflicts

# Manual marshaling/unmarshaling
raw_diff = {delete: ['removed_key'], 'updated_key': 'new_value'}
marshaled = differ.marshal(raw_diff)
print(marshaled)  # {'$delete': ['removed_key'], 'updated_key': 'new_value'}

unmarshaled = differ.unmarshal(marshaled)
print(unmarshaled)  # {delete: ['removed_key'], 'updated_key': 'new_value'}

# Safe round-trip serialization
import json

# Create diff with symbols
diff_with_symbols = differ.diff({'a': 1}, {'b': 2})
marshaled_diff = differ.marshal(diff_with_symbols)

# Serialize safely
json_str = json.dumps(marshaled_diff)
loaded_diff = json.loads(json_str)

# Unmarshal and apply
final_diff = differ.unmarshal(loaded_diff)
result = differ.patch({'a': 1}, final_diff)

Error Handling

JsonDiffer handles various error conditions and provides clear error messages:

from jsondiff import JsonDiffer
from json import JSONDecodeError

differ = JsonDiffer()

# Invalid syntax specification
try:
    invalid_differ = JsonDiffer(syntax='nonexistent')
except (KeyError, AttributeError) as e:
    print(f"Invalid syntax: {e}")

# Load errors with auto-loading
auto_differ = JsonDiffer(load=True)
try:
    result = auto_differ.diff('{"invalid": json}', '{"valid": "json"}')
except JSONDecodeError as e:
    print(f"JSON parsing error: {e}")

# Unpatch without symmetric syntax
compact_differ = JsonDiffer(syntax='compact')
try:
    result = compact_differ.unpatch({'a': 2}, {'a': 1})
except AttributeError as e:
    print(f"Unpatch not supported: {e}")

Install with Tessl CLI

npx tessl i tessl/pypi-jsondiff

docs

cli.md

core-operations.md

diff-syntaxes.md

index.md

jsondiff-class.md

serialization.md

tile.json