0
# Diff Syntaxes
1
2
Multiple output formats for representing differences between JSON structures. Each syntax is optimized for different use cases, from compact storage to human readability to bidirectional operations. Syntaxes are accessed by name through the `JsonDiffer` class or `diff()` function parameters.
3
4
## Capabilities
5
6
### Compact Syntax
7
8
The default syntax focusing on minimizing output size while maintaining full diff information. Ideal for storage, transmission, and most programmatic use cases. Use `syntax='compact'` with `diff()` or `JsonDiffer`.
9
10
```python { .api }
11
# Access via string name, not direct class import
12
syntax_name = 'compact' # Use with diff() or JsonDiffer(syntax='compact')
13
```
14
15
**Usage Examples:**
16
17
```python
18
from jsondiff import diff, JsonDiffer
19
from jsondiff.symbols import insert, delete, replace
20
21
# Dictionary changes
22
original = {'name': 'Alice', 'age': 30, 'skills': ['Python', 'Django']}
23
modified = {'name': 'Alice', 'age': 31, 'skills': ['Python', 'Django', 'Flask']}
24
25
result = diff(original, modified, syntax='compact')
26
# Result: {'age': 31, 'skills': {insert: [(2, 'Flask')]}}
27
28
# List with mixed operations
29
list1 = ['x', 'a', 'c', 'x']
30
list2 = ['a', 'b', 'c']
31
32
result = diff(list1, list2, syntax='compact')
33
# Result: {insert: [(1, 'b')], delete: [3, 0]}
34
35
# Complete replacement when similarity is low
36
dict1 = {'a': 1, 'b': 2, 'c': 3}
37
dict2 = {'x': 10, 'y': 20, 'z': 30}
38
39
result = diff(dict1, dict2, syntax='compact')
40
# Result: {replace: {'x': 10, 'y': 20, 'z': 30}}
41
```
42
43
### Explicit Syntax
44
45
Clear, human-readable syntax that explicitly labels all operations. Best for debugging, logging, and when diff readability is important. Use `syntax='explicit'` with `diff()` or `JsonDiffer`.
46
47
```python { .api }
48
# Access via string name, not direct class import
49
syntax_name = 'explicit' # Use with diff() or JsonDiffer(syntax='explicit')
50
```
51
52
**Usage Examples:**
53
54
```python
55
from jsondiff import diff
56
from jsondiff.symbols import insert, update, delete
57
58
# Dictionary with explicit operations
59
original = {'name': 'Alice', 'age': 30, 'temp': 'xyz'}
60
modified = {'name': 'Bob', 'age': 30, 'city': 'NYC'}
61
62
result = diff(original, modified, syntax='explicit')
63
# Result: {insert: {'city': 'NYC'}, update: {'name': 'Bob'}, delete: ['temp']}
64
65
# List operations clearly labeled
66
list1 = ['a', 'b', 'c']
67
list2 = ['a', 'x', 'c', 'd']
68
69
result = diff(list1, list2, syntax='explicit')
70
# Result: {insert: [(3, 'd')], update: {1: 'x'}}
71
72
# Set operations with clear labels
73
set1 = {'red', 'green', 'blue'}
74
set2 = {'red', 'yellow', 'purple'}
75
76
result = diff(set1, set2, syntax='explicit')
77
# Result: {add: {'yellow', 'purple'}, discard: {'green', 'blue'}}
78
```
79
80
### Symmetric Syntax
81
82
Maintains both original and modified values, enabling bidirectional operations. Supports both `patch()` and `unpatch()` operations for reversible diffs. Use `syntax='symmetric'` with `JsonDiffer`.
83
84
```python { .api }
85
# Access via string name, not direct class import
86
syntax_name = 'symmetric' # Use with JsonDiffer(syntax='symmetric')
87
```
88
89
**Usage Examples:**
90
91
```python
92
from jsondiff import diff, JsonDiffer
93
from jsondiff.symbols import insert, delete
94
95
# Dictionary with value history
96
original = {'name': 'Alice', 'age': 30, 'status': 'active'}
97
modified = {'name': 'Alice', 'age': 31, 'city': 'NYC'}
98
99
result = diff(original, modified, syntax='symmetric')
100
# Result: {'age': [30, 31], insert: {'city': 'NYC'}, delete: {'status': 'active'}}
101
102
# Bidirectional operations
103
differ = JsonDiffer(syntax='symmetric')
104
105
# Forward patch: original -> modified
106
patched = differ.patch(original, result)
107
print(patched) # {'name': 'Alice', 'age': 31, 'city': 'NYC'}
108
109
# Reverse patch: modified -> original
110
unpatched = differ.unpatch(patched, result)
111
print(unpatched) # {'name': 'Alice', 'age': 30, 'status': 'active'}
112
113
# List with symmetric changes
114
list1 = ['a', 'b', 'c']
115
list2 = ['a', 'x', 'c', 'd']
116
117
result = diff(list1, list2, syntax='symmetric')
118
# Result: {1: ['b', 'x'], insert: [(3, 'd')]}
119
120
# Complete replacement with both values
121
dict1 = {'a': 1}
122
dict2 = {'b': 2}
123
124
result = diff(dict1, dict2, syntax='symmetric')
125
# Result: [{'a': 1}, {'b': 2}]
126
```
127
128
### RightOnly Syntax
129
130
Focuses exclusively on the modified (right) values, particularly for lists. Useful when only the final state matters, not the path to get there. Use `syntax='rightonly'` with `diff()` or `JsonDiffer`.
131
132
```python { .api }
133
# Access via string name, not direct class import
134
syntax_name = 'rightonly' # Use with diff() or JsonDiffer(syntax='rightonly')
135
```
136
137
**Usage Examples:**
138
139
```python
140
from jsondiff import diff
141
142
# List shows final state only
143
original = ['Python', 'Django']
144
modified = ['Python', 'Django', 'Flask', 'React']
145
146
result = diff(original, modified, syntax='rightonly')
147
# Result: ['Python', 'Django', 'Flask', 'React']
148
149
# Dictionary still shows changes but focuses on final values
150
dict1 = {'name': 'Alice', 'skills': ['Python']}
151
dict2 = {'name': 'Alice', 'skills': ['Python', 'JavaScript', 'React']}
152
153
result = diff(dict1, dict2, syntax='rightonly')
154
# Result: {'skills': ['Python', 'JavaScript', 'React']}
155
156
# Useful for configuration updates where history doesn't matter
157
config1 = {'servers': ['srv1', 'srv2'], 'timeout': 30}
158
config2 = {'servers': ['srv1', 'srv3', 'srv4'], 'timeout': 60}
159
160
result = diff(config1, config2, syntax='rightonly')
161
# Result: {'servers': ['srv1', 'srv3', 'srv4'], 'timeout': 60}
162
```
163
164
### Custom Syntax Registration
165
166
Built-in syntaxes are accessible by name through `JsonDiffer` and can be extended with custom implementations.
167
168
```python { .api }
169
# Available built-in syntax names
170
available_syntaxes = ['compact', 'symmetric', 'explicit', 'rightonly']
171
```
172
173
**Usage Examples:**
174
175
```python
176
from jsondiff import JsonDiffer
177
178
# Use built-in syntax by name
179
differ = JsonDiffer(syntax='compact')
180
181
# Custom syntax class (advanced usage)
182
class VerboseJsonDiffSyntax:
183
def emit_value_diff(self, a, b, s):
184
if s == 1.0:
185
return {"status": "unchanged", "value": a}
186
else:
187
return {"status": "changed", "from": a, "to": b}
188
189
def emit_dict_diff(self, a, b, s, added, changed, removed):
190
# ... implement required method
191
pass
192
193
def emit_list_diff(self, a, b, s, inserted, changed, deleted):
194
# ... implement required method
195
pass
196
197
def emit_set_diff(self, a, b, s, added, removed):
198
# ... implement required method
199
pass
200
201
def patch(self, a, d):
202
# ... implement required method
203
pass
204
205
# Use custom syntax
206
custom_differ = JsonDiffer(syntax=VerboseJsonDiffSyntax())
207
result = custom_differ.diff({'x': 1}, {'x': 2})
208
# Result: {'x': {"status": "changed", "from": 1, "to": 2}}
209
```
210
211
## Syntax Comparison
212
213
| Syntax | Use Case | Patch Support | Unpatch Support | Output Size |
214
|--------|----------|---------------|-----------------|-------------|
215
| `compact` | General purpose, storage | ✓ | ✗ | Minimal |
216
| `explicit` | Human readable, debugging | ✗ | ✗ | Medium |
217
| `symmetric` | Bidirectional, version control | ✓ | ✓ | Large |
218
| `rightonly` | Final state focus | ✓ | ✗ | Medium |
219
220
## Error Handling
221
222
Syntax classes may raise exceptions for malformed diffs:
223
224
```python
225
from jsondiff import JsonDiffer
226
227
differ = JsonDiffer(syntax='symmetric')
228
229
# Invalid symmetric diff structure
230
try:
231
result = differ.patch({'a': 1}, "invalid_diff")
232
except Exception as e:
233
print(f"Invalid diff format: {e}")
234
```