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

How to insert a attribute with a dynamic value in many object or add an element dynamically in many array?

我想在许多 object(位于数组中)中添加一个属性,这个值将被动态获取。我使用下面的 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}][]

我的查询结果是这样的:

JSON 格式示例 1

[
 {
   "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 放在数组中的每个 object 上,然后将其从数组中删除,得到如下结果:

JSON 格式示例 2

[
  {
    "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
  }
]

在标题中我谈到了数组,实际上在我查询之后使用 .* 我可以转换数组中的 object 并且可能更容易地在每个数组中放置值(与 [= 匹配42=]s) 并使用 object 构造函数将数组重新转换为 object 。但我不知道该怎么做。你能帮我一下吗,或者如果可能的话至少告诉我一下。

PS:我只使用 JMESPath,所以我不想用任何其他包含 JMESPath 代码的语言(比如 javascript(在我的例子中)或 python 或别的)

快速回答 (TL;DR)

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

详细解答

上下文

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

问题

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

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

  • "almost" 不是我们想要的解决方案
  • 此代码...
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}]
  • 这不是我们想要的,因为:
    • 我们必须硬连线 MaxMouvCount 的值 3,技术上是 "cheating"
      • 这是作弊,因为我们想要一个动态值,而不是硬连线
    • 这会产生一个多余的元素,其中除 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'}]
  • 这不是我们想要的,因为我们在预期 3
  • 的地方得到了 None(又名 null

说明

  • 尝试 02 有效,如果 JMESPath 支持一个标记来引用 JSON 根对象
  • 另一个可行的查询是(例如,如果美元符号字符用作对 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

结论

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