-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathjsoncompare.py
More file actions
96 lines (73 loc) · 3.24 KB
/
jsoncompare.py
File metadata and controls
96 lines (73 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
from __future__ import annotations
import json
import sys
from pathlib import Path
from typing import Any
from urllib.request import urlopen
TYPE = "TYPE"
PATH = "PATH"
VALUE = "VALUE"
class Diff:
def __init__(self, first: Any, second: Any, with_values: bool = False):
self.difference: list[tuple[str, str]] = []
self.check(first, second, with_values=with_values)
def check(self, first: Any, second: Any, path: str = "", with_values: bool = False) -> None:
if with_values and second is not None and not isinstance(first, type(second)):
self.save_diff(f"{path} - {type(first).__name__}, {type(second).__name__}", TYPE)
return
if isinstance(first, dict):
for key, value in first.items():
new_path = key if not path else f"{path}.{key}"
if isinstance(second, dict) and key in second:
self.check(value, second[key], path=new_path, with_values=with_values)
else:
self.save_diff(new_path, PATH)
return
if isinstance(first, list):
for index, item in enumerate(first):
new_path = f"{path}[{index}]"
sec = second[index] if isinstance(second, list) and index < len(second) else None
if sec is None and second is not None:
self.save_diff(f"{new_path} - {type(item).__name__}", TYPE)
self.check(item, sec, path=new_path, with_values=with_values)
return
if with_values and second is not None and first != second:
self.save_diff(f"{path} - {first} | {second}", VALUE)
def save_diff(self, diff_message: str, diff_type: str) -> None:
item = (diff_type, diff_message)
if item not in self.difference:
self.difference.append(item)
def get_content_from_uri(uri: str) -> bytes:
with urlopen(uri) as response:
return response.read()
def get_content_from_file(file_path: str) -> str:
return Path(file_path).read_text(encoding="utf-8")
def get_content(location: str | dict[str, Any]) -> Any:
if isinstance(location, dict):
return location
content = get_content_from_uri(location) if location.startswith("http") else get_content_from_file(location)
return json.loads(content)
def compare(location1: str, location2: str) -> list[dict[str, str]]:
json1 = get_content(location1)
json2 = get_content(location2)
diff1 = Diff(json1, json2, True).difference
diff2 = Diff(json2, json1, False).difference
diffs: list[dict[str, str]] = []
for diff_type, message in diff1:
mapped = "CHANGED"
if diff_type == PATH:
mapped = "REMOVED"
diffs.append({"type": mapped, "message": message})
for _, message in diff2:
diffs.append({"type": "ADDED", "message": message})
return diffs
if __name__ == "__main__":
if len(sys.argv) != 3:
sys.exit("Usage: python jsoncompare.py <file-or-url-1> <file-or-url-2>")
location1 = sys.argv[1]
location2 = sys.argv[2]
diffs = compare(location1, location2)
if diffs:
print(f"\nFound differences comparing {location1} and {location2}")
for diff in diffs:
print(f"{diff['type']}: {diff['message']}")