提问人:njminchin 提问时间:11/8/2023 最后编辑:njminchin 更新时间:11/11/2023 访问量:72
如何比较两个遵循架构的JSON文件?
How to compare two JSON files that adhere to a schema?
问:
我有 JSON(用于 Ignition SCADA)在系统中定义“标签”,它本质上定义了一个树结构,其中结束树节点是标签本身,具有定义它们的配置,子树元素是文件夹。每个文件夹都有一个名为“tags”的键,其中包含其中包含的任何标签或文件夹的列表。
定义标记的 JSON 中最重要的部分是它的路径,它由 JSON 中沿它所在的文件夹路径的每个“name”字段组成,类似于文件的文件路径。路径的元素用“/”分隔,例如,对于下面的 TagA,其标记路径为“StackOverflow/FolderA/TagA”。
下面是 Ignition 设计器中的树:
下面是表示这些标记的 JSON:
{
"name": "StackOverflow",
"tagType": "Folder",
"tags": [
{
"name": "FolderA",
"tagType": "Folder",
"tags": [
{
"valueSource": "memory",
"dataType": "Int8",
"name": "TagA",
"tooltip": "This is tag a",
"value": 15,
"tagType": "AtomicTag",
"engUnit": "%"
},
{
"valueSource": "memory",
"dataType": "Int8",
"name": "TagB",
"tooltip": "This is tag b",
"value": 98,
"tagType": "AtomicTag",
"engUnit": "%"
}
]
}
]
}
如果我通过以下方式更改这些标签/文件夹:
- 添加新标签,
StackOverflow/FolderA/TagC
- 更改值
StackOverflow/FolderA/TagA.tooltip
- 更改值
StackOverflow/FolderA/TagA.value
- 删除
StackOverflow/FolderA/TagB
我想获得一份报告,作为我可以导出到 Excel 中的更改列表,例如:
[{'tagpath': 'StackOverflow/FolderA/TagC', 'changedFrom': None, 'changedTo': 'added'},
{'tagpath': 'StackOverflow/FolderA/TagA.tooltip', 'changedFrom': 'This is tag a', 'changedTo': 'This is tag a with changes'},
{'tagpath': 'StackOverflow/FolderA/TagA.value', 'changedFrom': 15, 'changedTo': 59},
{'tagpath': 'StackOverflow/FolderA/TagB', 'changedFrom': None, 'changedTo': 'deleted'}
]
以下是更改 JSON:
{
"name": "StackOverflow",
"tagType": "Folder",
"tags": [
{
"name": "FolderA",
"tagType": "Folder",
"tags": [
{
"valueSource": "memory",
"dataType": "Int8",
"name": "TagC",
"tooltip": "This is tag a with changes",
"value": 59,
"tagType": "AtomicTag",
"engUnit": "%"
},
{
"valueSource": "memory",
"dataType": "Int8",
"name": "TagA",
"tooltip": "This is tag a with changes",
"value": 59,
"tagType": "AtomicTag",
"engUnit": "%"
}
]
}
]
}
我不确定如何使用 Python 最好地比较这两个文件。我可以使用简单的 for 循环并手动比较 json 并跟踪差异来将一些东西组合在一起,但我想知道是否有更好更复杂的方法?
答:
1赞
SuNing
11/8/2023
#1
可能有些包会有用。如果你不想使用它,你可以使用迭代器来代替一个接一个。
from jsondiff import diff
json1 = {
"name": "Bob",
"age": 10,
"sex": "male"
}
json2 = {
"name": "Alice",
"age": 10,
"sex": "female"
}
difference1 = diff(json1, json2)
difference2 = diff(json2, json1)
print(difference1)
print(difference2)
结果: {'name': '爱丽丝', '': '女性'} {'name': '鲍勃', '': '男'}
我认为您可以使用它来构建您的更改日志。
评论
0赞
Community
11/8/2023
正如目前所写的那样,你的答案尚不清楚。请编辑以添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以在帮助中心找到有关如何写出好答案的更多信息。
0赞
njminchin
11/9/2023
嗯,虽然这不适用于比较整个 JSON 文件,但这引发了一个想法:我从未考虑过将其用于每个标签,而是至少获得标签级别的差异。然后,我需要编写自己的差异函数来检查标签是否被删除/添加,但这应该不会太糟糕。我会试一试,谢谢
2赞
Laurent Verweijen
11/11/2023
#2
这个问题可以用littletree(我是作者)来解决。
假设原始数据和修改后的数据分别作为嵌套字典存储在 和 中original
modified
from littletree import Node
original_tree = Node.from_dict(original, identifier_name="name", children_name="tags")
modified_tree = Node.from_dict(modified, identifier_name="name", children_name="tags")
# Collect changes in a list
changes = []
for diff_node in original_tree.compare(modified_tree).iter_tree():
diff_data = diff_node.data
if not diff_data:
continue # Data was the same
if "self" not in diff_data:
changes.append({"tagpath": str(diff_node.path), "from": None, "to": "added"})
elif "other" not in diff_data:
changes.append({"tagpath": str(diff_node.path), "from": None, "to": "removed"})
else:
original_data, modified_data = diff_data["self"], diff_data["other"]
for key, original_value in original_data.items():
modified_value = modified_data[key]
if original_value != modified_value:
changes.append({"tagpath": f"{diff_node.path}.{key}",
"from": original_value,
"to": modified_value})
for change in changes:
print(change)
结果如下所示:
{'tagpath': '/StackOverflow/FolderA/TagA.tooltip', 'from': 'This is tag a', 'to': 'This is tag a but changed'}
{'tagpath': '/StackOverflow/FolderA/TagA.value', 'from': 15, 'to': 16}
{'tagpath': '/StackOverflow/FolderA/TagB', 'from': None, 'to': 'removed'}
{'tagpath': '/StackOverflow/FolderA/TagC', 'from': None, 'to': 'added'}
评论
0赞
njminchin
11/13/2023
这看起来很酷,但是当我运行它时,我没有看到正确的结果。这是我得到的:{'tagpath': '/StackOverflow/FolderA/TagA.tagType', 'from': 'Folder', 'to': 'AtomicTag'} {'tagpath': '/StackOverflow/FolderA/TagB', 'from': None, 'to': 'removed'} {'tagpath': '/StackOverflow/FolderA/TagC', 'from': None, 'to': 'added'}
1赞
Laurent Verweijen
11/13/2023
最近已修复,但我看到修复程序尚未发布。我现在刚刚发布了它。
0赞
njminchin
11/13/2023
太棒了,我现在让它工作了,谢谢!:)我在更改部分对其进行了一些修改,因为它不处理另一个部分中不存在的键,但这就是我更改的全部内容。超级酷,谢谢!
0赞
njminchin
11/14/2023
我不确定在哪里最好问这个问题,但我想将您的库用于另一个 json 模式差异。在本例中,是每个节点的 prop 的子键,例如 .这可以使用吗?identifier_name
meta
meta.name
0赞
Laurent Verweijen
11/15/2023
也不知道该问哪里。随意在 github.com/lverweijen/littletree/issues 上创建问题 它不是开箱即用的,但这可能会有所帮助: ''' def to_tree(data): “”“标识符存储在 node[”meta“][”name“].”“” identifier = data[“meta”][“name”] children = [to_tree(child) for child in data.pop(“tags”, [])] return Node(data, identifier=identifier, children=children) '''
评论