如果使用 JMESPath 的第一个键不存在,那么选择另一个 JSON 键的最佳方法是什么?

What's the best way to pick another JSON key if the first not exists using JMESPath?

JSON有一些产品数据,例如:

{
  "sku": 123,
  "product": {
    "name": "Some name",
    "images": {
      "normalImage": "http://somelink.com/1.jpg",
      "bigImage": "http://somelink.com/1b.jpg"
    }
  }
}

我想选择图片link,但是bigImage只存在于某些产品中,所以有时我需要选择normalImage

显而易见的解决方案如下所示:

jmespath.search('product.images.bigImage') or jmespath.search('product.images.normalImage')

但我觉得可以做得更好。如何使用 JMESPath 语法以最佳方式做到这一点?

您可以创建一个 CustomFunctions class 来执行此操作,类似于 GitHub 页面中给出的示例。

from jmespath import search
from jmespath import functions
from jmespath import Options

from json import loads

class CustomFunctions(functions.Functions):

    # Method that selects 'bigImage' key value if it exists
    # Otherwise return 'normalImage' value
    # dict.get() is perfect for this, since it returns a default value if a key doesn't exist
    # Use type 'object' since thats the equivalant type to a Python dictionary in JSON
    # Make sure to decorate function signature as well to indicate types
    # Make sure to also put _func_ before your function name
    @functions.signature({'types': ['object']})
    def _func_choose_key(self, d):
        return d.get('bigImage', d['normalImage'])

if __name__ == "__main__":

    # Get custom function options
    options = Options(custom_functions=CustomFunctions())

    # Test method which runs JMESPath query with custom function
    def test(json):
        json_dict = loads(json)
        return search('product.images | choose_key(@)', json_dict, options=options)


    # TEST 1 - bigImage key exists
    json1 = """{
        "sku": 123,
        "product": {
            "name": "Some name",
            "images": {
                "normalImage": "http://somelink.com/1.jpg",
                "bigImage": "http://somelink.com/1b.jpg"
            }
        }
    }"""

    print("Test1: %s" % test(json1))


    # TEST 2 - bigImage key doesn't exist
    json2 = """{
        "sku": 123,
        "product": {
            "name": "Some name",
            "images": {
                "normalImage": "http://somelink.com/1.jpg"
            }
        }
    }"""


    print("Test2: %s" % test(json2))

打印出以下结果:

Test1: http://somelink.com/1b.jpg  # When bigImage key exists
Test2: http://somelink.com/1.jpg   # When bigImage key doesn't exist

如果 JMESPath 变得太复杂,我们总是可以使用旧的标准字典方法:

def test2(json):
    json_dict = loads(json)
    images = json_dict["product"]["images"]
    return images.get("bigImage", images["normalImage"])

# TEST 1 - bigImage key exists
json1 = """{
    "sku": 123,
    "product": {
        "name": "Some name",
        "images": {
            "normalImage": "http://somelink.com/1.jpg",
            "bigImage": "http://somelink.com/1b.jpg"
        }
    }
}"""

print("Test1: %s" % test2(json1))


# TEST 2 - bigImage key doesn't exist
json2 = """{
    "sku": 123,
    "product": {
        "name": "Some name",
        "images": {
            "normalImage": "http://somelink.com/1.jpg"
        }
    }
}"""


print("Test2: %s" % test2(json2))

它也打印相同的结果:

Test1: http://somelink.com/1b.jpg  # When bigImage key exists
Test2: http://somelink.com/1.jpg   # When bigImage key doesn't exist

以下仅使用 JMESPath 语法怎么样?

product.images.[bigImage, normalImage][?@]|[0]

我们的想法是,我们将要使用的所有图像按优先顺序排列,过滤掉缺失的图像,然后选择剩余阵列中的第一项。

警告 - 这不会区分丢失和 null(或其他 "falsey" 值,例如空字符串),因此如果这对您来说很重要,您可能需要稍微调整一下你的具体情况。