简体   繁体   中英

How to print out a value in a json, with only 1 'searchstring'

payload = {
    "data": {
        "name": "John",
        "surname": "Doe"
    }
}

print(payload["data"]["name"])

I want to print out the value of 'name' inside the json. I know the way to do it like above. But is there also a way to print out the value of 'name' with only 1 'search string'?

I'm looking for something like this

print(payload["data:name"])

Output:
John

If you were dealing with nested attributes of an object I would suggest operator.attrgetter , however, the itemgetter in the same module does not seems to support nested key access. It is fairly easy to implement something similar tho:

payload = {
    "data": {
        "name": "John",
        "surname": "Doe",
        "address": {
            "postcode": "667"
        }
    }
}


def get_key_path(d, path):
    # Remember latest object
    obj = d
    # For each key in the given list of keys
    for key in path:
        # Look up that key in the last object
        if key not in obj:
            raise KeyError(f"Object {obj} has no key {key}")
        # now we know the key exists, replace
        # last object with obj[key] to move to 
        # the next level
        obj = obj[key]
    return obj

print(get_key_path(payload, ["data"]))
print(get_key_path(payload, ["data", "name"]))
print(get_key_path(payload, ["data", "address", "postcode"]))

Output:

$ python3 ~/tmp/so.py
{'name': 'John', 'surname': 'Doe', 'address': {'postcode': '667'}}
John
667

You can always later decide on a separator character and use a single string instead of path , however, you need to make sure this character does not appear in a valid key. For example, using | , the only change you need to do in get_key_path is:

def get_key_path(d, path):
    obj = d
    for key in path.split("|"):  # Here
    ...

There isn't really a way you can do this by using the 'search string'. You can use the get() method, but like getting it using the square brackets, you will have to first parse the dictionary inside the data key.

You could try creating your own function that uses something like:

str.split(sep=None, maxsplit=-1)

Return a list of the words in the string, using sep as the delimiter string. If maxsplit is given, at most maxsplit splits are done (thus, the list will have at most maxsplit+1 elements). If maxsplit is not specified or -1 , then there is no limit on the number of splits (all possible splits are made).

def get_leaf_value(d, search_string):
    if ":" not in search_string:
        return d[search_string]
    next_d, next_search_string = search_string.split(':', 1)
    return get_value(d[next_d], next_search_string)

payload = {
    "data": {
        "name": "John",
        "surname": "Doe"
    }
}

print(payload["data"]["name"])
print(get_leaf_value(payload, "data:name"))

Output:

John
John

This approach will only work if your data is completely nested dictionaries like in your example (ie, no lists in non-leaf nodes) and : is not part of any keys obviously.

Here is an alternative. Maybe an overkill, it depends.

jq uses a single "search string" - an expression called 'jq program' by the author - to extract and transform data. It is a powerful tool meaning the jq program can be quite complex. Reading a good tutorial is almost a must.

import pyjq

payload = ... as posted in the question ...
expr = '.data.name'
name = pyjq.one(expr, payload)  # "John"

The original project (written in C) is located here . The python jq libraries are build on top of that C code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM