如何在多个对象中插入具有动态值的属性或在多个数组中动态添加元素?

时间:2019-07-26 15:44:42

标签: jmespath

我想在许多对象(位于数组中)中添加一个属性,并且该值将动态获取。我使用下面的JSON,并且已经进行了查询以提取所需的内容。我们将从该查询的结果开始。

首先是我的整个JSON:

[  
   {  
      "Nature":"lol",
      "EV":"lol",
      "Moves":[  
         {  
"Move":"OHKOmove",
            "Max":100,
            "Min":15
         },
         {  
"Move":"cacaz",
            "Max":35,
            "Min":20
         }
      ]
   },
   {  
      "Nature":"loi",
      "EV":"lal",
      "Moves":[  
         {  
"Move":"caca1",
            "Max":100,
            "Min":3
         },
{  
"Move":"caca2",
            "Max":100,
            "Min":3
         }
      ]
   },
   {  
      "Nature":"loi2",
      "EV":"lal",
      "Moves":[  
         {  
"Move":"caca1",
            "Max":100,
            "Min":3
         },
{  
"Move":"caca2",
            "Max":100,
            "Min":3
         },
{  
"Move":"caca3",
            "Max":100,
            "Min":3
         }
      ]
   },
   {  
      "Nature":"loi3",
      "EV":"lil",
      "Moves":[  
         {  
"Move":"caca1",
            "Max":100,
            "Min":3
         },
{  
"Move":"caca2",
            "Max":100,
            "Min":3
         },
{  
"Move":"caca3",
            "Max":100,
            "Min":3
         }
      ]
   }
]

然后我的查询:[?(length(Moves[?Max == `100`]) > `1`)].{Nature: Nature, EV: EV, Moves: Moves[?Max == `100`].Move, MovesCount: length(Moves[?Max == `100`].Move)} | [@,{MaxMouvCount: max_by(@, &MovesCount).MovesCount}][]

查询的结果如下:

[
 {
   "Nature": "loi",
   "EV": "lal",
   "Moves": [
     "caca1",
     "caca2"
   ],
   "MovesCount": 2
 },
 {
   "Nature": "loi2",
   "EV": "lal",
   "Moves": [
     "caca1",
     "caca2",
     "caca3"
   ],
   "MovesCount": 3
 },
 {
   "Nature": "loi3",
   "EV": "lil",
   "Moves": [
     "caca1",
     "caca2",
     "caca3"
   ],
   "MovesCount": 3
 },
 {
   "MaxMouvCount": 3
 }
]

想法是将属性"MaxMouvCount": 3放在数组中的每个对象上,然后将其从数组中删除以得到如下结果:

[
  {
    "Nature": "loi",
    "EV": "lal",
    "Moves": [
      "caca1",
      "caca2"
    ],
    "MovesCount": 2,
    "MaxMouvCount": 3
  },
  {
    "Nature": "loi2",
    "EV": "lal",
    "Moves": [
      "caca1",
      "caca2",
      "caca3"
    ],
    "MovesCount": 3,
    "MaxMouvCount": 3
  },
  {
    "Nature": "loi3",
    "EV": "lil",
    "Moves": [
      "caca1",
      "caca2",
      "caca3"
    ],
    "MovesCount": 3,
    "MaxMouvCount": 3
  }
]

在标题中我谈到了数组,实际上是在查询后使用.*可以转换数组中的对象,并且可以更容易地将每个数组中的值(与对象匹配)并使用对象构造函数。但是我不知道该怎么做。您能帮我还是至少告诉我是否可能。

PS:我只使用JMESPath,所以我不想用其他包含JMESPath代码的语言(例如javascript(就我而言)或python或其他内容)来回答

1 个答案:

答案 0 :(得分:1)

快速解答(TL; DR)

  • 通常,使用JMESPath轻松转换JSON很容易
    • 一个轻松的键是利用经过特别规范化的JSON结构,以最佳地与JMESPath结合使用
    • 一个轻松的键是知道何时在JSON中使用字典(又名对象//关联数组//映射)名称/值对,以使JSON的所有部分都能明确引用
  • 不幸的是,此特定问题的目标是使用标准JMESPath 不可行,因为JMESPath在current-node上下文中缺少引用JSON数据根的令牌

详细答案

上下文

  • JMESPath查询语言
  • python 3.x使用JMESPath 0.9.4 [但是任何JMESPath引擎都可以使用]

问题

  • 场景:
    • DeveloperSObosskay972希望将JSON数据从一种表示形式转换为另一种表示形式
    • DeveloperSObosskay972希望仅依靠JMESPath表达式来完成转换
    • DeveloperSObosskay972希望在另一部分中引用JSON结构的一部分,以允许动态交叉引用数据结构

尝试01(不是我们真正想要的)

  • 不是我们想要的“几乎”解决方案
  • 此代码...
import jmespath
vdata001aa = """<<json.load(JSON Format Example 1)>>"""
vresult = jmespath.compile('@|[*].{"Nature":@.Nature,"EV":@.EV,"Moves":@.Moves,"MovesCount":@.MovesCount,"MaxMouvCount":`3`}').search(vdata001aa)
pprint.pprint(vresult)
  • 产生这个结果...
[{'EV': 'lal',
  'MaxMouvCount': 3,
  'Moves': ['caca1', 'caca2'],
  'MovesCount': 2,
  'Nature': 'loi'},
 {'EV': 'lal',
  'MaxMouvCount': 3,
  'Moves': ['caca1', 'caca2', 'caca3'],
  'MovesCount': 3,
  'Nature': 'loi2'},
 {'EV': 'lil',
  'MaxMouvCount': 3,
  'Moves': ['caca1', 'caca2', 'caca3'],
  'MovesCount': 3,
  'Nature': 'loi3'},
 {'EV': None,
  'MaxMouvCount': 3,
  'Moves': None,
  'MovesCount': None,
  'Nature': None}]
  • 这不是我们想要的,因为:
    • 我们不得不为3设置MaxMouvCount的值,这在技术上是“作弊”
      • 之所以作弊是因为我们想要一个 dynamic 值,而不是一个 hard-wired
    • 这会产生一个多余的元素,其中除MaxMouvCount之外的每个值都是null(python恰好将其称为None
    • 我们只想要三个元素,而不是四个

尝试02(不是我们真正想要的)

  • Attempt 01不能正常工作的原因是,原始的JSON结构未针对JMESPath进行规范化
  • 为了解决这个问题,我们将字典名称/值对添加到原始数据中
  • 通过这种方法,我们使JSON数据的每个元素成为附加在字典键上的值(又称javascript对象名-值对)
    • 这里我们使用的术语dictionary在其他情况下称为objecthashassociative arraymapping
    • 我们不在乎术语,而是能够将顶级JSON的所有部分称为名称/值对
尝试02 //第1部分(重新格式化JSON格式示例1)
  • 重新格式化您的原始JSON JSON Format Example 1,使其看起来像这样
  • 经过重新格式化的JSON将使您数据的所有部分都明确可访问
{
"jsontop": {
    "settings_info": {
      "MaxMouvCount": 3
    },
    "nature_table": [
     {
       "Nature": "loi",
       "EV": "lal",
       "Moves": [
         "caca1",
         "caca2"
       ],
       "MovesCount": 2
     },
     {
       "Nature": "loi2",
       "EV": "lal",
       "Moves": [
         "caca1",
         "caca2",
         "caca3"
       ],
       "MovesCount": 3
     },
     {
       "Nature": "loi3",
       "EV": "lil",
       "Moves": [
         "caca1",
         "caca2",
         "caca3"
       ],
       "MovesCount": 3
     }
    ]
}
尝试02 //第2部分(运行无痛苦的JMESPath查询以获取所需内容)
  • 此代码...
import jmespath
vdata001aa  = """<<json.load(**RE-NORMALIZED** JSON Format Example 1)>>"""
vresult     = jmespath.compile('@|jsontop.nature_table[*].{"Nature":@.Nature,"EV":@.EV,"Moves":@.Moves,"MovesCount":@.MovesCount,"MaxMouvCount":jsontop.settings_info.MaxMouvCount}').search(vdata001aa)
pprint.pprint(vresult)
pass
  • 产生这个结果...
[{'EV': 'lal',
  'MaxMouvCount': None,
  'Moves': ['caca1', 'caca2'],
  'MovesCount': 2,
  'Nature': 'loi'},
 {'EV': 'lal',
  'MaxMouvCount': None,
  'Moves': ['caca1', 'caca2', 'caca3'],
  'MovesCount': 3,
  'Nature': 'loi2'},
 {'EV': 'lil',
  'MaxMouvCount': None,
  'Moves': ['caca1', 'caca2', 'caca3'],
  'MovesCount': 3,
  'Nature': 'loi3'}]
  • 这不是我们想要的,因为我们在预期的None达到了null(又名3

说明

    如果JMESPath支持令牌引用JSON根对象,
  • 尝试02 成功
  • 一个可行的替代查询是(例如,如果美元符号字符用作对JSON数据根的引用)
import jmespath
vdata001aa  = """<<json.load(**RE-NORMALIZED** JSON Format Example 1)>>"""
vresult     = jmespath.compile('@|jsontop.nature_table[*].{"Nature":@.Nature,"EV":@.EV,"Moves":@.Moves,"MovesCount":@.MovesCount,"MaxMouvCount":$.jsontop.settings_info.MaxMouvCount}').search(vdata001aa)
pprint.pprint(vresult)
pass

结论

  • 尝试01和尝试02表明当前(稳定)的JMESPath版本不能完全满足要求
  • 您将必须突破JMESPath才能从JSON中获取所需的动态值并填充重新格式化的数据
  • 或者,您将不得不向JMESPath本身添加一个扩展,这可能比使用托管语言的功能更不受欢迎