【问题标题】:Updating complex JSON object in Python在 Python 中更新复杂的 JSON 对象
【发布时间】:2016-09-01 15:49:35
【问题描述】:

我正在使用 Python (v3.5) 抓取一个复杂的 MongoDB 文档,我应该更新其中的一些值,这些值分散在对象周围并且在结构中没有特定模式并将其保存回不同的 MongoDB收藏。对象如下所示:

# after json.loads(mongo_db_document) my dict looks like this
notification = {
    '_id': '570f934f45213b0d14b1256f',
    'key': 'receipt',
    'label': 'Delivery Receipt',
    'version': '0.0.1',
    'active': True,
    'children': [
        {
            'key': 'started',
            'label': 'Started',
            'children': [
                'date',
                'time',
                'offset'
            ]
        },
        {
            'key': 'stop',
            'label': 'Ended',
            'children': [
                'date',
                'time',
                'offset'
            ]
        },
        {
            'label': '1. Particulars',
            'template': 'formGroup',
            'children': [
                {
                    'children': [
                        {
                            'key': 'name',
                            'label': '2.1 Name',
                            'value': '********** THIS SHOULD BE UPDATED **********',
                            'readonly': 'true'
                        },
                        {
                            'key': 'ims_id',
                            'label': '2.2 IMS Number',
                            'value': '********** THIS SHOULD BE UPDATED **********',
                            'readonly': 'true'
                        }
                    ]
                },
                {
                    'children': [
                        {
                            'key': 'type',
                            'readonly': '********** THIS SHOULD BE UPDATED **********',
                            'label': '2.3 Type',
                            'options': [
                                {
                                    'label': 'Passenger',
                                    'value': 'A37'
                                },
                                {
                                    'label': 'Cargo',
                                    'value': 'A35'
                                },
                                {
                                    'label': 'Other',
                                    'value': '********** THIS SHOULD BE UPDATED **********'
                                }
                            ]
                        }
                    ]
                }
            ]
        },
        {
            'template': 'formGroup',
            'key': 'waste',
            'label': '3. Waste',
            'children': [
                {
                    'label': 'Waste',
                    'children': [
                        {
                            'label': 'Plastics',
                            'key': 'A',
                            'inputType': 'number',
                            'inputAttributes': {
                                'min': 0
                            },
                            'value': '********** THIS SHOULD BE UPDATED **********'
                        },
                        {
                            'label': 'B. Oil',
                            'key': 'B',
                            'inputType': 'number',
                            'inputAttributes': {
                                'min': 0
                            },
                            'value': '********** THIS SHOULD BE UPDATED **********'
                        },
                        {
                            'label': 'C. Operational',
                            'key': 'C',
                            'inputType': 'number',
                            'inputAttributes': {
                                'min': 0
                            },
                            'value': '********** THIS SHOULD BE UPDATED **********'
                        }
                    ]
                }
            ]
        },
        {
            'template': 'formRow',
            'children': [
                'empty',
                'signature'
            ]
        }
    ],
    'filter': {
        'timestamp_of_record': [
            'date',
            'time',
            'offset'
        ]
    }
}

我最初的想法是将占位符(如 $var_name)放在我需要更新值的地方,并使用 Python 的 string.Template 加载字符串,但不幸的是,这种方法会破坏同一个 MongoDB 的其他用户的很多东西出于某种原因记录。

有没有一种解决方案可以简单地修改这种对象而无需“硬编码”路径来查找我需要更新的值?

【问题讨论】:

  • 您是在问如何动态找到这些值的路径吗?
  • 我知道由于某种原因您无法再次加载、修改和另存为 json?
  • @C14L 好吧,是的,你可以这样表述我的问题 :) 我可以编写一个函数来遍历整个 dict 并寻找每一种可能性,但我在想是否有有更简单的解决方案吗?
  • 你能提供更多细节吗?也许是一个例子?这种“假设性”的问题是不可能找到答案的。
  • @Roberto 是的,同样的对象用在 JavaScript、PostgreSQL 中,并且在另一端被解析,所以现在放置像 $var_name 这样的占位符非常棘手。

标签: python json mongodb python-3.x


【解决方案1】:

这是我几年前编写的一个小脚本 - 我用它来查找一些非常长且令人不安的 JSON 中的条目。诚然,它并不漂亮,但也许对您有帮助?
你可以在 Bitbucket 上找到脚本,herehere 是代码)。 不幸的是,它没有记录在案;我猜当时我并不太相信其他人会使用它。
无论如何,如果您想尝试一下,请将脚本保存在您的工作目录中,然后使用以下内容:

from RecursiveSearch import Retriever

def alter_data(json_data, key, original, newval):
    '''
    Alter *all* values of said keys
    '''
    retr = Retriever(json_data)
    for item_no, item in enumerate(retr.__track__(key)): # i.e. all 'value'
        # Pick parent objects with a last element False in the __track__() result,
        # indicating that `key` is either a dict key or a set element
        if not item[-1]: 
            parent = retr.get_parent(key, item_no)
            try:
                if parent[key] == original:
                    parent[key] = newval
            except TypeError:
                # It's a set, this is not the key you're looking for
                pass

if __name__ == '__main__':
    alter_data(notification, key='value', 
               original = '********** THIS SHOULD BE UPDATED **********',
               newval = '*UPDATED*')

很遗憾,正如我所说,该脚本没有很好的文档记录,所以如果您想尝试它并需要更多信息,我很乐意提供。

【讨论】:

  • PS:您还可以使用parent 元素进一步验证该值是否需要更新,即仅当它还包含label 键或其他检查时才更新。
【解决方案2】:

不确定我是否理解正确,但这将动态查找所有键“值”和“只读”并打印出寻址字段的路径。

def findem(data, trail):
    if isinstance(data, dict):
        for k in data.keys():
            if k in ('value', 'readonly'):
                print("{}['{}']".format(trail, k))
            else:
                findem(data[k], "{}['{}']".format(trail, k))
    elif isinstance(data, list):
        for k in data:
            findem(k, '{}[{}]'.format(trail, data.index(k)))

if __name__ == '__main__':
    findem(notification, 'notification')

notification['children'][2]['children'][0]['children'][0]['readonly']
notification['children'][2]['children'][0]['children'][0]['value']
notification['children'][2]['children'][0]['children'][1]['readonly']
notification['children'][2]['children'][0]['children'][1]['value']
notification['children'][2]['children'][1]['children'][0]['readonly']
notification['children'][2]['children'][1]['children'][0]['options'][0]['value']
notification['children'][2]['children'][1]['children'][0]['options'][1]['value']
notification['children'][2]['children'][1]['children'][0]['options'][2]['value']
notification['children'][3]['children'][0]['children'][0]['value']
notification['children'][3]['children'][0]['children'][1]['value']
notification['children'][3]['children'][0]['children'][2]['value']

【讨论】:

    【解决方案3】:

    向 JSON 对象添加另一个列表。该列表中的每个项目都是导致要更改的值的键列表。此类列表的一个示例是:['children', 2, 'children', 'children', 0, 'value']。 然后,要访问该值,您可以使用循环:

    def change(json, path, newVal):
        cur = json
        for key in path[:-1]:
            cur = cur[key]
        cur[path[-1]] = newVal
    
    path = notification['paths'][0]
    #path, for example, could be ['children', 2, 'children', 'children', 0, 'value']
    newVal = 'what ever you want'
    change(notification, path, newVal)
    

    【讨论】:

    • 这看起来很有趣。我会尝试一下,并在成功或失败后报告:)
    • 请报告@errata :)
    猜你喜欢
    • 2020-12-15
    • 1970-01-01
    • 1970-01-01
    • 2021-08-23
    • 1970-01-01
    • 2010-12-05
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    相关资源
    最近更新 更多