处理和验证包含重复密钥的JSON

ua4mk5z4  于 2022-12-20  发布在  其他
关注(0)|答案(1)|浏览(115)

我正在尝试验证我们收到的“json”文件,因为生成这些文件的源代码存在一些问题,如果不进行重大修改,这些问题是无法纠正的。“json”中有许多对象是无效的。下面是一个例子,它重用了端口命名的键。
示例无效json文件

[
  {"TimeStamp": "2021-11-28", "Address": { "port": "eth2 present", "port": "eth0 present",  "port": "eth1 present" }},
  {"TimeStamp": "2021-11-29", "CamStatus": 1},
  {"TimeStamp": "2021-11-30", "CamDone": 0}
]

我尝试做的是首先确定哪些行是无效的。从那里,如果可能的话,我想清除它们。
使用json.load()时,我看到了一个奇怪的行为,即解析了无效的json,但排除了两个键/值对。

with open(r"sample.json") as json_file:
    content = json.load(json_file)
content

结果

[{'TimeStamp': '2021-11-28', 'Address': {'port': 'eth1 present'}},
 {'TimeStamp': '2021-11-29', 'CamStatus': 1},
 {'TimeStamp': '2021-11-30', 'CamDone': 0}]

为了识别损坏的行,我使用json.loads()编写了下面的代码,但是我也得到了意外的行为,即第二个对象被读为无效。

with open("sample.json") as json_file:
    for line in json_file:
        try:
            a = json.loads(line)
            print('valid JSON', line)
        except:
            print('invalid JSON', line)

产出

invalid JSON [
invalid JSON {"TimeStamp": "2021-11-28", "Address": { "port": "eth2 present", "port": "eth0 present",  "port": "eth1 present" }},
invalid JSON {"TimeStamp": "2021-11-29", "CamStatus": 1},
valid JSON   {"TimeStamp": "2021-11-30", "CamDone": 0}
invalid JSON ]

我尝试做的是生成一个如下结构:

[{'TimeStamp': '2021-11-28', 'Address': {'port0': 'eth1 present', 'port1': 'eth2 present', 'port2': 'eth3 present'}},
 {'TimeStamp': '2021-11-29', 'CamStatus': 1},
 {'TimeStamp': '2021-11-30', 'CamDone': 0}]

有什么想法、模块、示例代码可以帮助我吗?

hgc7kmma

hgc7kmma1#

可以给JSON解析器一个参数,告诉它在收集由键/值对组成的对象时使用标准dict以外的对象。如果在该参数中提供multidict构造函数,那么最终得到的结构将保留原始JSON文件中的所有信息,即使该文件包含重复的键(因此是无效的JSON)。
完成此操作后,就可以查询数据结构,以查找内部MultiDict对象之一中存在多个同名键的位置,并标记出现这种情况的行。
下面是一个简单的例子,它可以针对您所展示的数据执行此操作。我预计它需要针对实际情况进行修改,但它演示了以这种方式使用JSON解析器,然后在结果中标记无效的键/值对结构的基本思想:

data = """
[
  {"TimeStamp": "2021-11-29", "CamStatus": 1},
  {"TimeStamp": "2021-11-29", "CamStatus": 1},
  {"TimeStamp": "2021-11-28", "Address": { "port": "eth2 present", "port": "eth0 present",  "port": "eth1 present" }},
  {"TimeStamp": "2021-11-29", "CamStatus": 1},
  {"TimeStamp": "2021-11-28", "Address": { "port": "eth2 present", "port1": "eth0 present",  "port2": "eth1 present" }},
  {"TimeStamp": "2021-11-28", "Address": { "port": "eth2 present", "port": "eth0 present",  "port2": "eth1 present" }},
  {"TimeStamp": "2021-11-30", "CamDone": 0}
]
"""

from multidict import MultiDict
import json

r = json.loads(data, object_pairs_hook=MultiDict)

for i, entry in enumerate(r):
    for key, value in entry.items():
        if isinstance(value, MultiDict):
            keys = set()
            for key2, value2 in value.items():
                if key2 in keys:
                    print(f"Line {i+1} is invalid")
                    break
                else:
                    keys.add(key2)

结果:

Line 3 is invalid
Line 6 is invalid

您可以遍历结果结构并构建一个新的结构,当遇到重复键时,您可以在其中处理它们,从而"修复"传入的数据结构。您可以将键重命名为(在本例中)port1port2等,或者您可以将这种情况下的值收集到列表中。然后可以使用json.dump写出该结构,以生成有效的JSON文件。
下面的代码采用前一种方法将读结构转换回只包含普通dict对象的结构。注意,新键名中的下划线是必要的,以避免与现有名称冲突。这是可以避免的。但逻辑必须稍微复杂一点,以允许与结尾为数字的现有键名发生冲突。是好的演示,因为它清楚地表明哪些键被重命名。

fixed = []
for entry in r:
    new_entry = {}
    for key, value in entry.items():
        if isinstance(value, MultiDict):
            new_entry[key] = {}
            keys = {}
            for key2, value2 in value.items():
                if key2 in keys:
                    keys[key2] += 1
                    next_key = key2 + '_' + str(keys[key2])
                else:
                    keys[key2] = 0
                    next_key = key2
                new_entry[key][next_key] = value2
        else:
            new_entry[key] = value
    fixed.append(new_entry)

pprint(fixed)

结果:

[{'CamStatus': 1, 'TimeStamp': '2021-11-29'},
 {'CamStatus': 1, 'TimeStamp': '2021-11-29'},
 {'Address': {'port': 'eth2 present',
              'port_1': 'eth0 present',
              'port_2': 'eth1 present'},
  'TimeStamp': '2021-11-28'},
 {'CamStatus': 1, 'TimeStamp': '2021-11-29'},
 {'Address': {'port': 'eth2 present',
              'port1': 'eth0 present',
              'port2': 'eth1 present'},
  'TimeStamp': '2021-11-28'},
 {'Address': {'port': 'eth2 present',
              'port2': 'eth1 present',
              'port_1': 'eth0 present'},
  'TimeStamp': '2021-11-28'},
 {'CamDone': 0, 'TimeStamp': '2021-11-30'}]

下面是使用第二种方法的代码:

fixed = []
for entry in r:
    new_entry = {}
    for key, value in entry.items():
        if isinstance(value, MultiDict):
            new_entry[key] = {}
            keys = {}
            for key2, value2 in value.items():
                if key2 in keys:
                    keys[key2].append(value2)
                    new_entry[key][key2] = keys[key2]
                else:
                    keys[key2] = [value2]
                    new_entry[key][key2] = value2
        else:
            new_entry[key] = value
    fixed.append(new_entry)

pprint(fixed)

结果:

[{'CamStatus': 1, 'TimeStamp': '2021-11-29'},
 {'CamStatus': 1, 'TimeStamp': '2021-11-29'},
 {'Address': {'port': ['eth2 present', 'eth0 present', 'eth1 present']},
  'TimeStamp': '2021-11-28'},
 {'CamStatus': 1, 'TimeStamp': '2021-11-29'},
 {'Address': {'port': 'eth2 present',
              'port1': 'eth0 present',
              'port2': 'eth1 present'},
  'TimeStamp': '2021-11-28'},
 {'Address': {'port': ['eth2 present', 'eth0 present'],
              'port2': 'eth1 present'},
  'TimeStamp': '2021-11-28'},
 {'CamDone': 0, 'TimeStamp': '2021-11-30'}]

相关问题