简体   繁体   English

如果表达式存在,则使用 Jmespath 过滤 JSON 并返回值,如果它不返回 None/Null (python)

[英]Filter JSON using Jmespath and return value if expression exist, if it doesn't return None/Null (python)

How can I get JMESPath to only return the value in a json if it exists, if it doesn't exist return none/null.我怎样才能让 JMESPath 只返回 json 中的值(如果它存在),如果它不存在则返回 none/null。 I am using JMESPath in a python application, below is an example of a simple JSON data.我在 python 应用程序中使用 JMESPath,下面是一个简单的 JSON 数据示例。

{
  "name": "Sarah",
  "region": "south west",
  "age": 21,
  "occupation": "teacher",
  "height": 145,
  "education": "university",
  "favouriteMovie": "matrix",
  "gender": "female",
  "country": "US",
  "level": "medium",
  "tags": [],
  "data": "abc",
  "moreData": "xyz",
  "logging" : {
    "systemLogging" : [ {
      "enabled" : true,
      "example" : [ "this", "is", "an", "example", "array" ]
    } ]
  }
}

For example I want it to check if the key "occupation" contains the word "banker" if it doesn't return null .例如,如果不返回null我希望它检查键"occupation"包含单词"banker"

In this case if I do jmespath query "occupation == 'banker'" I would get false .在这种情况下,如果我执行 jmespath 查询"occupation == 'banker'"我会得到false However for more complicated jmespath queries like "logging.systemLogging[?enabled == `false`]" this would result in an empty array [] because it doesn't exist, which is what I want.然而,对于更复杂的 jmespath 查询,如"logging.systemLogging[?enabled == `false`]"这将导致一个空数组[]因为它不存在,这就是我想要的。

The reason I want it to return none or null is because in another part of the application (my base class) I have code that checks if the dictionary/json data will return a value or not, this piece of code iterates through an array of dictionaries/ json data like the one above.我希望它返回nonenull的原因是因为在应用程序的另一部分(我的基类)中,我有代码来检查字典/json 数据是否会返回一个值,这段代码遍历一个数组字典/ json 数据,如上面的那个。

One thing I've noticed with JMESPath is that it is inconsistent with its return value.我注意到 JMESPath 的一件事是它与其返回值不一致。 In more complicated dictionaries I am able to achieve what I want but from simple dictionaries I can't, also If you used a methods, eg starts_with , it returns a boolean but if you just use an expression it returns the value you are looking for if it exists otherwise it will return None or an empty array.在更复杂的字典中,我能够实现我想要的但从简单的字典我不能,如果你使用了一个方法,例如starts_with ,它返回一个布尔值,但如果你只使用一个表达式,它返回你正在寻找的值如果它存在,否则它将返回None或一个空数组。

This is traditionally accomplished by:这在传统上是通过以下方式完成的:

dictionary = json.loads(my_json)
dictionary.get(key, None) # None is the default value that is returned.

That will work if you know the exact structure to expect from the json.如果您知道 json 的确切结构,这将起作用。 Alternatively you can make two calls to JMESpath, using one to try to get the value / None / empty list, and one to run the query you want.或者,您可以对 JMESpath 进行两次调用,一次尝试获取值 / 无 / 空列表,一次运行您想要的查询。

The problem is that JMESpath is trying to answer your query: Does this structure contain this information pattern?问题是 JMESpath 试图回答您的查询:此结构是否包含此信息模式? It makes sense that the result of such a query should be True/False.这种查询的结果应该是 True/False 是有道理的。 If you want to get something like an empty list back, you need to modify your query to ask "Give me back all instances where this structure contains the information I'm looking for" or "Give me back the first instance where this structure contains the information I'm looking for."如果你想得到一个空列表之类的东西,你需要修改你的查询来询问“给我这个结构包含我正在寻找的信息的所有实例”或“给我这个结构包含的第一个实例我正在寻找的信息。”

Filters in JMESPath do apply to arrays (or list, to speak in Python). JMESPath 中的过滤器确实适用于数组(或列表,用 Python 说)。
So, indeed, your case is not a really common one.所以,确实,你的情况并不是一个很常见的情况。

This said, you can create an array out of a hash (or dictionary, to speak in Python again) using the to_array function.to_array ,您可以使用to_array函数从散列(或字典,再次用 Python 说)创建一个数组。
Then, since you do know you started from a hash, you can select back the first element of the created array, and indeed, if the array ends up being empty, it will return a null .然后,由于您确实知道您是从哈希开始的,因此您可以选择回创建的数组的第一个元素,实际上,如果数组最终为空,它将返回一个null

To me, at least, it looks consistant, an array can be empty [] , but an empty object is a null .至少对我来说,它看起来是一致的,一个数组可以是空[] ,但一个空对象是一个null

To use this trick, though, you will also have to reset the projection you created out of the array, with the pipe expression :但是,要使用此技巧,您还必须使用管道表达式重置您从数组中创建的投影:

Projections are an important concept in JMESPath.投影是 JMESPath 中的一个重要概念。 However, there are times when projection semantics are not what you want.但是,有时投影语义不是您想要的。 A common scenario is when you want to operate of the result of a projection rather than projecting an expression onto each element in the array.一个常见的场景是当您想要对投影的结果进行运算而不是将表达式投影到数组中的每个元素上时。 For example, the expression people[*].first will give you an array containing the first names of everyone in the people array.例如,表达式people[*].first将为您提供一个数组,其中包含 people 数组中每个人的名字。 What if you wanted the first element in that list?如果您想要该列表中的第一个元素怎么办? If you tried people[*].first[0] that you just evaluate first[0] for each element in the people array, and because indexing is not defined for strings, the final result would be an empty array, [] .如果您尝试people[*].first[0] ,您只是为 people 数组中的每个元素计算first[0] ,并且由于没有为字符串定义索引,最终结果将是一个空数组[] To accomplish the desired result, you can use a pipe expression, <expression> | <expression>要实现所需的结果,您可以使用管道表达式<expression> | <expression> <expression> | <expression> , to indicate that a projection must stop. <expression> | <expression> ,表示必须停止投影。

Source: https://jmespath.org/tutorial.html#pipe-expressions来源: https : //jmespath.org/tutorial.html#pipe-expressions


And so, with all this, the expression ends up being:因此,有了这一切,表达式最终是:

to_array(@)[?occupation == `banker`]|[0]

Which gives这使

null

On your example JSON, while the expression在您的示例 JSON 上,而表达式

to_array(@)[?occupation == `teacher`]|[0]

Would return your existing object, so:将返回您现有的对象,因此:

{
  "name": "Sarah",
  "region": "south west",
  "age": 21,
  "occupation": "teacher",
  "height": 145,
  "education": "university",
  "favouriteMovie": "matrix",
  "gender": "female",
  "country": "US",
  "level": "medium",
  "tags": [],
  "data": "abc",
  "moreData": "xyz",
  "logging": {
    "systemLogging": [
      {
        "enabled": true,
        "example": [
          "this",
          "is",
          "an",
          "example",
          "array"
        ]
      }
    ]
  }
}

And following this trick, all your other test will probably start to work eg按照这个技巧,你所有的其他测试可能会开始工作,例如

  • to_array(@)[?starts_with(occupation, `tea`)]|[0]
    will give you back your object会把你的对象还给你
  • to_array(@)[?starts_with(occupation, `ban`)]|[0]
    will give you a null会给你一个null

And if you only need the value of the occupation property, as you are falling back to a hash now, it is as simple as doing, eg如果你只需要occupation属性的价值,因为你现在回退到散列,就像做一样简单,例如

  • to_array(@)[?starts_with(occupation, `tea`)]|[0].occupation
    Which gives这使
    "teacher"
  •  to_array(@)[?starts_with(occupation, `ban`)]|[0].occupation
    Which gives这使
    null

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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