【问题标题】:(Python) merge new and existing JSON with deduplication(Python) 将新的和现有的 JSON 与重复数据删除合并
【发布时间】:2015-12-18 09:59:00
【问题描述】:

我正在使用 Python 查询 API,此 API 发送最后 X 事件的 JSON,我想保留它发送给我的内容的历史记录。

这就是 API 发送的内容,我的平面历史文件中有相同类型的元素(但有更多相同的对象)。 API 和我的最终文件没有设置字典的键。

[{
    "Item1": "01234",
    "Item2": "Company",
    "Item3": "XXXXXXXXX",
    "Item4": "",
    "Item5": "2015-12-17T12:00:01.553",
    "Item6": "2015-12-18T12:00:00"
},
{
    "Item1": "01234",
    "Item2": "Company2",
    "Item3": "XXXXXXX",
    "Item4": null,
    "Item5": "2015-12-17T16:49:23.76",
    "Item6": "2015-12-18T11:00:00",
}]

如果 API 的元素不在原始文件中,我该如何添加它们? 我有一个打开/关闭文件的框架,但对处理没有太多想法。

main_file=open("History.json","r")
new_items=[]
api_data=requests.get(#here lies the api address and the header)
#here should be the deplucation/processing process
for item in api_data
    if item not in main_file 
        new_items.append(item)
main_file.close()
try:
    file_updated = open("History.json",'w')
    file_updated.write(new_items + main_file)
    file_updated.close()
    print("File updated")
except :
    print("Error writing file")

编辑:我使用 json to object 方法来做到这一点:

from collections import namedtuple
Event = namedtuple('Event', 'Item1, Item2, Item3, Item4, Item5, Item6')
def parse_json_events(text):
    events = [ Event(**k) for k in json.loads(text) ]
    return events
if path.exists('Mainfile.json'):
    with open('Mainfile.json') as data_file:
        local_data = json.load(data_file)
        print(local_data.text) #debug purposes
        events_local=parse_json_events(local_data.text)
else:
    events_local=[]
events_api=parse_json_events(api_request.text)
inserted_events=0 
for e in events_api[::-1]:
    if e not in events_local:
        events_local.insert(0, e)
        inserted_events=inserted_events+1
print("inserted elements %d"  % inserted_events)
print(events_local) # this is OK, gives me a list of events
print(json.dump(events_local)) # this ... well... I want the list of object to be serialized but I get this error : 

TypeError: dump() 缺少 1 个必需的位置参数:'fp'

【问题讨论】:

    标签: python json merge


    【解决方案1】:

    通常,您通过定义带有/不带有第三​​方工具(如 Avro、Thrift 等)的架构来解决此类问题。基本上,您从 API 获得的每条记录都需要以您正在使用的编程语言翻译成实体。

    我们以这个 JSON 对象为例:

    {
        "Item1": "01234",
        "Item2": "Company",
        "Item3": "XXXXXXXXX",
        "Item4": "",
        "Item5": "2015-12-17T12:00:01.553",
        "Item6": "2015-12-18T12:00:00"
    },
    

    如果你有类似的架构

    Company(object):
      company_number = ...
      name = ...
      # other fields
    

    然后,您需要做的就是对原始数据进行序列化和反序列化。

    理想情况下,您会从 API 读取 JSON 响应,然后您可以简单地将每个 json 对象拆分为模式对象(使用或不使用工具)。在伪代码中:

    api_client = client(http://..., )
    response = api_client.get("/resources")
    json = response.json 
    companies = parse_json_companies(json) # list of Company objects
    

    此时,处理从 api 获得的数据真的很容易。您应该对存储在文件系统上的文件执行相同的操作。加载您的文件并反序列化记录(到公司对象)。然后,比较对象就很容易了,因为它们就像“普通”的 Python 对象一样,因此您可以执行比较等操作。

    例如:

    from collections import namedtuple
    import json
    Company = namedtuple('Company', 'Item1, Item2, Item3, Item4, Item5, Item6')
    def parse_json_companies(text):
      companies = [Company(**k) for k in json.loads(text)]
      return companies
    
    >>> companies = parse_json_companies(response.json)
    >>> companies
    [Company(Item1='01234', Item2='Company', Item3='XXXXXXXXX', Item4=u'', Item5='2015-12-17T12:00:01.553', Item6='2015-12-18T12:00:00'), Company(Item1='01234', Item2='Company2', Item3='XXXXXXX', Item4=None, Item5='2015-12-17T16:49:23.76', Item6='2015-12-18T11:00:00')]
    

    .dump(obj, fp) 出错后更新。

    如果您收到 json.dump 错误,请参阅documentation。它明确指出 objfp 是必需的参数。

    使用此转换表将 obj 作为 JSON 格式的流序列化为 fp(支持 .write() 的类似文件的对象)。

    因此,您需要传递一个支持 .write 的对象(例如,以写入模式打开的文件)。

    【讨论】:

    • 这似乎是我想要做的,不过,我自己做 JSON 到对象解析似乎是在重新创建轮子,我认为会有另一种方法来本地或使用 python 库。
    • 好吧,您需要做的就是编写您的 json 解码器。 docs.python.org/2/library/json.html
    • 我更新了答案。我希望你现在能更好地理解如何做到这一点。
    • 哇,谢谢,但我似乎在第 4 行遇到了语法无效的问题……先生,您打开“[”但从不关闭“]”不是有问题吗?
    • companies= [Company(**k) for k in json.loads(text))] ^ SyntaxError: invalid syntax
    【解决方案2】:

    我认为解决此问题的最佳方法是考虑您的数据结构。此时您似乎正在使用与 api 相同的数据结构。

    这些项目字段中有Id 吗?如果是这样,请使用该字段进行重复数据删除。但是对于这个例子,我将使用公司名称。

    with open('history.json') as f:
        historic_data = json.load(f)
        api_data = requests.get()
        for item in api_data:
            historic_data[item['Item2']] = item
        f.write(json.dumps(historic_data))
    

    每次这种情况下的名称已经存在于字典中时,它都会被覆盖。如果名称不存在,它将被添加。

    【讨论】:

    • @kuzko,如果它回答了你的问题,请告诉我 :)
    • 不幸的是,不,API 没有给我任何唯一键来引用对象,所以我需要做一个完整的对象到完整的对象比较,公司名称在其他对象中重复,就像所有其他领域一样......
    猜你喜欢
    • 2021-12-06
    • 2020-11-23
    • 2020-05-10
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 2018-10-19
    • 2021-10-27
    • 1970-01-01
    相关资源
    最近更新 更多