簡體   English   中英

如何在不知道位置的情況下從嵌套的 json 返回特定的鍵值對?

[英]How to return specific key value pair from nested json without knowing location?

我想在不知道確切位置的情況下獲取嵌套 json 文件中特定鍵的值。 所以基本上查看所有鍵(和嵌套鍵)直到找到匹配項,並返回字典 {match: "value"} 嵌套json_data

{
  "$id": "1",
  "DataChangedEntry": {
    "$id": "2",
    "PathProperty": "/",
    "Metadata": null,
    "PreviousValue": null,
    "CurrentValue": {
      "CosewicWsRefId": {
        "Value": "QkNlrjq2HL9bhTQqU8-qH"
      },
      "Date": {
        "Value": "2022-05-20T00:00:00Z"
      },
      "YearSentToMinister": {
        "Value": "0001-01-01T00:00:00"
      },
      "DateSentToMinister": {
        "Value": "0001-01-01T00:00:00"
      },
      "Order": null,
      "Type": {
        "Value": "REGULAR"
      },
      "ReportType": {
        "Value": "NEW"
      },
      "Stage": {
        "Value": "ASSESSED"
      },
      "State": {
        "Value": "PUBLISHED"
      },
      "StatusAndCriteria": {
        "Status": {
          "Value": "EXTINCT"
        },
        "StatusComment": {
          "EnglishText": null,
          "FrenchText": null
        },
        "StatusChange": {
          "Value": "NOT_INITIALIZED"
        },
        "StatusCriteria": {
          "EnglishText": null,
          "FrenchText": null
        },
        "ApplicabilityOfCriteria": {
          "ApplicabilityCriteriaList": []
        }
      },
      "Designation": null,
      "Note": null,
      "DomainEvents": [],
      "Version": {
        "Value": 1651756761385.1248
      },
      "Id": {
        "Value": "3z3XlCkaXY9xinAbK5PrU"
      },
      "CreatedAt": {
        "Value": 1651756761384
      },
      "ModifiedAt": {
        "Value": 1651756785274
      },
      "CreatedBy": {
        "Value": "G@a"
      },
      "ModifiedBy": {
        "Value": "G@a"
      }
    }
  },
  "EventAction": "Create",
  "EventDataChange": {
    "$ref": "2"
  },
  "CorrelationId": "3z3XlCkaXY9xinAbK5PrU",
  "EventId": "WGxlewsUAHayLHZ2LHvFk",
  "EventTimeUtc": "2022-05-06T13:15:31.7463355Z",
  "EventDataVersion": "1.0.0",
  "EventType": "AssessmentCreatedInfrastructure"
}

期望的返回值是來自 json_data["DataChangedEntry"]["CurrentValue"]["Date"]["Value"] 的值:

"2022-05-20T00:00:00Z"

到目前為止,我已經嘗試了一個遞歸函數,但它一直返回None

match_dict = {}
def recursive_json(data,attr,m_dict):
    for k,v in data.items():
        if k == attr:
            for k2,v2 in v.items():
                m_dict = {attr, v2}
                print('IF: ',m_dict)
                return m_dict
        elif isinstance(v,dict):
            return recursive_json(v,attr,m_dict)


print('RETURN: ',recursive_json(json_data, "Date", match_dict))

輸出:

RETURN:  None

我嘗試刪除第二個return語句,它現在在函數中打印我想要的值,但仍然返回None

match_dict = {}
def recursive_json(data,attr,m_dict):
    for k,v in data.items():
        if k == attr:
            for k2,v2 in v.items():
                m_dict = {attr, v2}
                print('IF: ',m_dict)
                return m_dict
        elif isinstance(v,dict):
            recursive_json(v,attr,m_dict)


print('RETURN: ',recursive_json(json_data, "Date", match_dict))

輸出:

IF:  {'Date', '2022-05-20T00:00:00Z'}
RETURN:  None

我不明白為什么它一直返回None 有沒有更好的方法來返回我想要的值?

潛在的問題是:我們如何在一個循環中進行多次遞歸調用,如果其中任何一個返回有用的東西,則return遞歸結果,否則失敗?

如果我們盲目地在循環內return ,那么只能進行一次遞歸調用。 無論它返回什么,都會在這個級別返回。 如果它沒有找到有用的結果,我們就得不到有用的結果。

如果我們一味地不在循環內返回,那么返回的值就無關緊要了。 當前調用中沒有任何東西使用它們,所以我們將完成循環,進行所有遞歸調用,到達函數的末尾......因此隱式返回None

當然,解決這個問題的方法是檢查遞歸調用是否返回了有用的東西。 如果是這樣,我們可以返回它; 否則,我們繼續前進。 如果我們到達終點,那么我們會發出信號,表明我們找不到任何有用的東西——這樣,如果我們被遞歸調用,調用者可以做正確的事情。

假設None不能是“有用”的值,我們自然可以將其用作信號。 我們甚至不必在最后顯式地返回它。

在修正了一些其他的錯別字之后(我們不應該覆蓋全局內置的dict名稱,而且無論如何我們不需要命名我們在開始時傳入的dict,並且參數應該是m_dict以便在我們正確定義時進行遞歸調用),我們得到:

def recursive_json(data, attr, m_dict):
    for k,v in data.items():
        if k == attr:
            for k2,v2 in v.items():
                m_dict = {attr, v2}
                print('IF: ', m_dict)
                return m_dict
        elif isinstance(v,dict):
            result = recursive_json(v, attr, m_dict)
            if result:
                return result

# call it:
recursive_json(json_data, "Date", {})

我們可以看到打印了調試跟蹤,並且也返回了值。

讓我們稍微改進一下:

首先for k2,v2 in v.items():沒有任何意義。 同樣,我們每次調用只能return一次,因此這將跳過第一個之后的字典中的任何值。 直接返回v會更好。 此外, m_dict參數實際上並不能幫助實現邏輯; 我們不會在調用之間修改它。 使用set作為我們的返回值是沒有意義的,因為它基本上是無序的; 我們關心這里的順序。 最后,我們不再需要調試跟蹤。 這給了我們:

def recursive_json(data, attr):
    for k, v in data.items():
        if k == attr:
            return attr, v
        elif isinstance(v,dict):
            result = recursive_json(v, attr)
            if result:
                return result

為了更漂亮,我們可以將基本案例與遞歸案例分開,並為每個案例使用更優雅的工具。 要檢查是否有任何鍵匹配,我們可以簡單地使用in運算符進行檢查。 要遞歸並返回第一個富有成效的結果,內置的next很有用。 我們得到:

def recursive_json(data, attr):
    if not isinstance(data, dict):
        # reached a leaf, can't search in here.
        return None
    if attr in data:
        return k, data[k]
    candidates = (recursive_json(v, attr) for v in data.values())
    try:
        # the first non-None candidate, if any.
        return next(c for c in candidates if c is not None)
    except StopIteration:
        return None # all candidates were None.

看起來你正在嘗試寫這樣的東西:

from json import loads
from typing import Any

test_json = """
{
  "a": {
    "b": {
      "value": 1
    }
  },
  "b": {
    "value": 2
  },
  "c": {
    "b": {
      "value": 3
    },
    "c": {
      "value": 4
    }
  },
  "d": {}
}
"""

json_data = loads(test_json)


def find_value(data: dict, attr: str, depth_first: bool=True) -> (bool, Any):
    # assumes data is a dict, with 'value' attributes for the attr to be found
    # returns [whether value was found]: bool, [actual value]: Any
    for k, v in data.items():
        if k == attr and 'value' in v:
            return True, v['value']
        elif depth_first and isinstance(v, dict):
            if (t := find_value(v, attr, depth_first))[0]:
                return t
    if not depth_first:
        for _, v in data.items():
            if isinstance(v, dict) and (t := find_value(v, attr, depth_first))[0]:
                return t
    return False, None


# returns True, 1 - first 'b' with a 'value', depth-first
print(find_value(json_data, 'b'))
# returns True, 2 - first 'b' with a 'value', breadth-first
print(find_value(json_data, 'b', False))
# returns True, 4 - first 'c' with a 'value' - the 'c' at the root level has no 'value'
print(find_value(json_data, 'c'))
# returns False, None - no 'd' with a value
print(find_value(json_data, 'd'))
# returns False, None - no 'e' in data
print(find_value(json_data, 'e'))

您自己的函數可以返回None因為您實際上並沒有返回遞歸調用將返回的值。 函數的默認返回值為None

但是,您的代碼也沒有考慮到找不到任何東西的情況。

(注意:此解決方案僅適用於 Python 3.8 或更高版本,因為它使用了海象運算符:= - 當然,如果沒有它,編寫它並不難,但這留給讀者作為練習

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM