Поиск ключа внутри вложенных списков внутри вложенных словарей - PullRequest
0 голосов
/ 17 июня 2020

Исходное сообщение: от Go до json построчно, включая неизвестные вложенные массивы и объекты

Я пытаюсь найти конкретное значение c во всех вложенных списки и вложенные словари внутри словаря JSON. Структура словаря не всегда известна. Вложенный словарь может иметь вложенный список.

Я ищу ключ Date и не должен иметь String . Пример key = Date равен True , но если key = DateString условие не выполняется.

Код:

def ConvertTimestamp(my_list_of_dicts: list):

        for e in my_list_of_dicts:
            # check top level keys whose values are not a list
            keys_with_date = [k for k, v in e.items() if 'Date' in k and type(v) and 'String' not in k != list]

            for k1 in keys_with_date:
                e[k1] = 'found'


            # check top level keys whose values are a list
            keys_with_lists = [k for k, v in e.items() if type(v) == list]

            for k1 in keys_with_lists:
                for i, d in enumerate(e[k1]):
                    for k2, v in d.items():
                        if 'Date' in k2 and 'String' not in k2:
                            e[k1][i][k2] = 'found'

        return my_list_of_dicts

Данные

test_data = [{
            "PurchaseOrderID": "aaff50c2-05d5-4943-9a37-421d1b326dc3",
            "PurchaseOrderNumber": "PO-0001",
            "DateString": "2020-06-04T00:00:00",
            "Date": "2020-06-04T02:00:00.000000",
            "DeliveryDateString": "2020-06-11T00:00:00",
            "DeliveryDate": "2020-06-11T02:00:00.000000",
            "DeliveryAddress": "",
            "AttentionTo": "",
            "Telephone": "",
            "DeliveryInstructions": "",
            "HasErrors": false,
            "IsDiscounted": true,
            "Reference": "",
            "Type": "PURCHASEORDER",
            "CurrencyRate": 1.0,
            "CurrencyCode": "EUR",
            "Contact": {
                "ContactID": "31dcd998-026662967",
                "ContactStatus": "ACTIVE",
                "Name": "Test",
                "FirstName": "",
                "LastName": "",
                "Addresses": [],
                "Phones": [],
                "UpdatedDateUTC": "/Date(1591272554130+0000)/",
                "ContactGroups": [],
                "DefaultCurrency": "EUR",
                "ContactPersons": [],
                "HasValidationErrors": false
            },
            "BrandingThemeID": "86a1c878-7b2ed792b224",
            "Status": "DELETED",
            "LineAmountTypes": "Exclusive",
            "SubTotal": 1000.0,
            "TotalTax": 0.0,
            "Total": 1000.0,
            "UpdatedDateUTC": "2020-06-04T12:14:26.527000",
            "HasAttachments": false }]

Результат должен быть

[{
            "PurchaseOrderID": "aaff50c2-05d5-4943-9a37-421d1b326dc3",
            "PurchaseOrderNumber": "PO-0001",
            "DateString": "2020-06-04T00:00:00",
            "Date": "2020-06-04T02:00:00.000000",
            "DeliveryDateString": "2020-06-11T00:00:00",
            "DeliveryDate": "2020-06-11T02:00:00.000000",
            "DeliveryAddress": "",
            "AttentionTo": "",
            "Telephone": "",
            "DeliveryInstructions": "",
            "HasErrors": false,
            "IsDiscounted": true,
            "Reference": "",
            "Type": "PURCHASEORDER",
            "CurrencyRate": 1.0,
            "CurrencyCode": "EUR",
            "Contact": {
                "ContactID": "31dcd998-026662967",
                "ContactStatus": "ACTIVE",
                "Name": "Test",
                "FirstName": "",
                "LastName": "",
                "Addresses": [],
                "Phones": [],
                "UpdatedDateUTC": "2020-06-03T09:55:30.000000",
                "ContactGroups": [],
                "DefaultCurrency": "EUR",
                "ContactPersons": [],
                "HasValidationErrors": false
            },
            "BrandingThemeID": "86a1c878-7b2ed792b224",
            "Status": "DELETED",
            "LineAmountTypes": "Exclusive",
            "SubTotal": 1000.0,
            "TotalTax": 0.0,
            "Total": 1000.0,
            "UpdatedDateUTC": "2020-06-04T12:14:26.527000",
            "HasAttachments": false }]

Ответы [ 2 ]

0 голосов
/ 17 июня 2020

Вот как вы можете рекурсивно проходить через объект, делая обновления.

Для общности рекурсивная функция принимает два внешне определенных вызываемых объекта в дополнение к рекурсивному объекту:

  • функция «тестера ключей», которая должна принимать ключ (строку) и возвращать логическое значение, которое используется для определения того, какие ключи должны обновлять свои значения

  • функция "replacer", которая должна принимать значение и возвращать новое значение

from datetime import date
from pprint import pprint
from copy import deepcopy
import re


def do_replacements(obj, key_tester, replacer):
    """
    recursing through the nested list/dict structure,
    and wherever key_tester(key) yields True, 
    use replacer function to generate the new value
    """
    if isinstance(obj, dict):
        for k, v in obj.items():
            if key_tester(k):
                obj[k] = replacer(v)
            else:
                do_replacements(v, key_tester, replacer)
    elif isinstance(obj, list):
        for item in obj:
            do_replacements(item, key_tester, replacer)


def fix_time(ts):
    """
    replace the timestamp if it fits a particular pattern
    (based on code in original question)
    """
    pattern = '\(|\)'
    if not re.search(pattern, ts):
        return ts
    format = '%Y-%m-%dT%H:%M:%S.%f'
    ts_utc = re.split(pattern, ts)[1]
    ts_utc = ts_utc[:ts_utc.find("+")]
    return date.fromtimestamp(float(ts_utc)/1000).strftime(format)


test_data =    [{'PurchaseOrderID': 'aaff50c2-05d5-4943-9a37-421d1b326dc3', 'PurchaseOrderNumber': 'PO-0001', 'DateString': '2020-06-04T00:00:00', 'Date': '2020-06-04T02:00:00.000000', 'DeliveryDateString': '2020-06-11T00:00:00', 'DeliveryDate': '2020-06-11T02:00:00.000000', 'DeliveryAddress': '', 'AttentionTo': '', 'Telephone': '', 'DeliveryInstructions': '', 'HasErrors': False, 'IsDiscounted': True, 'Reference': '', 'Type': 'PURCHASEORDER', 'CurrencyRate': 1.0, 'CurrencyCode': 'EUR', 'Contact': {'ContactID': '31dcd998-026662967', 'ContactStatus': 'ACTIVE', 'Name': 'Test', 'FirstName': '', 'LastName': '', 'Addresses': [], 'Phones': [], 'UpdatedDateUTC': '/Date(1591272554130+0000)/', 'ContactGroups': [], 'DefaultCurrency': 'EUR', 'ContactPersons': [], 'HasValidationErrors': False}, 'BrandingThemeID': '86a1c878-7b2ed792b224', 'Status': 'DELETED', 'LineAmountTypes': 'Exclusive', 'SubTotal': 1000.0, 'TotalTax': 0.0, 'Total': 1000.0, 'UpdatedDateUTC': '2020-06-04T12:14:26.527000', 'HasAttachments': False}]


func = lambda k: "Date" in k and "String" not in k

output = deepcopy(test_data)
do_replacements(output, func, fix_time)
pprint(output)

дает:

[{'AttentionTo': '',
  'BrandingThemeID': '86a1c878-7b2ed792b224',
  'Contact': {'Addresses': [],
              'ContactGroups': [],
              'ContactID': '31dcd998-026662967',
              'ContactPersons': [],
              'ContactStatus': 'ACTIVE',
              'DefaultCurrency': 'EUR',
              'FirstName': '',
              'HasValidationErrors': False,
              'LastName': '',
              'Name': 'Test',
              'Phones': [],
              'UpdatedDateUTC': '2020-06-04T00:00:00.000000'},
  'CurrencyCode': 'EUR',
  'CurrencyRate': 1.0,
  'Date': '2020-06-04T02:00:00.000000',
  'DateString': '2020-06-04T00:00:00',
  'DeliveryAddress': '',
  'DeliveryDate': '2020-06-11T02:00:00.000000',
  'DeliveryDateString': '2020-06-11T00:00:00',
  'DeliveryInstructions': '',
  'HasAttachments': False,
  'HasErrors': False,
  'IsDiscounted': True,
  'LineAmountTypes': 'Exclusive',
  'PurchaseOrderID': 'aaff50c2-05d5-4943-9a37-421d1b326dc3',
  'PurchaseOrderNumber': 'PO-0001',
  'Reference': '',
  'Status': 'DELETED',
  'SubTotal': 1000.0,
  'Telephone': '',
  'Total': 1000.0,
  'TotalTax': 0.0,
  'Type': 'PURCHASEORDER',
  'UpdatedDateUTC': '2020-06-04T12:14:26.527000'}]

(Примечание: показанный здесь результат красиво напечатанный объект python, а не JSON, хотя похож.)

0 голосов
/ 17 июня 2020

Так как вы не знаете, какова структура словарей, она может находиться на произвольном количестве уровней.

Кроме того, ожидаемый результат на самом деле не показывает, что вы хотите делать с словарь однажды найден, поэтому я только что добавил эти словари в список.

В таких случаях помогает рекурсия.

def search_dict(d, results):
    for k,v in d.items():
        if 'Date' in k and 'String' not in k:
            # Do what you want with `d` here.
            # Your "Result should be" didn't really explain the
            # found part, but if it gets here it means you found it.

            # Appended to results as we want to continue searching for more.
            # Appending (k,v,d) where d is the dictionary containing
            # this key and value, incase you wanted that too. 
            # Adjust this accordingly.
            results.append((k,v,d))

        if isinstance(v, dict):
            search_dict(v, results)

        if isinstance(v, list):
            search_list(v, results)

def search_list(l, results):
    for item in l:
        if not isinstance(item, dict):
            continue # don't care about things that aren't dictionaries

        if isinstance(item, list):
            search_list(item, results)

        if isinstance(item, dict):
            search_dict(item, results)

def ConvertTimestamp(my_list_of_dicts: list):
    results = []
    search_list(my_list_of_dicts, results)
    return results
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...