2

我探索了几种现有的 JSON 查询语言,例如JMESPathJsonPathJSONiq。不幸的是,它们似乎都无法以通用的方式支持我的用例。

基本上,我从不同的 Web 服务收到不同类型的响应。我需要让用户能够在二维数组中重新映射响应以利用我们的可视化工具。基于新格式,用户可以决定如何在现有小部件之间显示他的数据。非常类似于完全在 UI 上管理的可定制仪表板。

无论如何,我的输入看起来像:

{
  "category_1": [
    {
      "name": "medium",
      "count": 10
    },
    {
      "name": "high",
      "count": 20
    }
  ],
  "category_2": [
    {
      "name": "medium",
      "count": 30
    },
    {
      "name": "high",
      "count": 40
    }
  ]
}

预期输出:

[
  {
    "name": "medium",
    "count": 10,
    "category": "1"
  },
  {
    "name": "high",
    "count": 20,
    "category": "1"
  },
  {
    "name": "medium",
    "count": 30,
    "category": "2"
  },
  {
    "name": "high",
    "count": 40,
    "category": "2"
  }
]

我越接近 JMESPath,但我的查询根本不是动态的。用户需要了解可能的分组类别。

查询如下所示:[ category_1[].{name: name, count: count, category: '1'}, category_2[].{name: name, count: count, category: '2'} ] | []

换句话说,我需要一个足够强大的 JSON 查询语言来执行这个 JavaScript 代码:

const output = flatMap(input, (value, key) => {
  return value.map(x => {
    return { ...x, category: key };
  });
});

有什么想法吗?

4

4 回答 4

2

这在 JMESPath (0.15.x) 中目前确实是不可能的。还有其他符合规范的 JMESPath 包(需要付出一些额外的努力)可以满足您的要求。使用 NPM 包@metrichor/jmespath(一个打字稿实现),您可以使用您需要的功能对其进行扩展,如下所示:


import {
  registerFunction,
  search,
  TYPE_ARRAY,
  TYPE_OBJECT
} from '@metrichor/jmespath';

registerFunction(
  'flatMapValues',
  ([inputObject]) => {
    return Object.entries(inputObject).reduce((flattened, entry) => {
      const [key, value]: [string, any] = entry;

      if (Array.isArray(value)) {
        return [...flattened, ...value.map(v => [key, v])];
      }
      return [...flattened, [key, value]];
    }, [] as any[]);
  },
  [{ types: [TYPE_OBJECT, TYPE_ARRAY] }],
);

使用这些扩展函数,JMESPath 表达式现在看起来像这样,以将键重新映射到每个值:

search("flatMapValues(@)[*].merge([1], {category: [0]})", {
  "category_1": [
    {
      "name": "medium",
      "count": 10
    },
    {
      "name": "high",
      "count": 20
    }
  ],
  "category_2": [
    {
      "name": "medium",
      "count": 30
    },
    {
      "name": "high",
      "count": 40
    }
  ]
});

// OUTPUTS:

[
  {
    category: 'category_1',
    count: 10,
    name: 'medium',
  },
  {
    category: 'category_1',
    count: 20,
    name: 'high',
  },
  {
    category: 'category_2',
    count: 30,
    name: 'medium',
  },
  {
    category: 'category_2',
    count: 40,
    name: 'high',
  },
]

也就是说你可以注册上面写的函数并使用它

于 2020-05-21T06:11:38.430 回答
1

最后,使用 Zorba 实现通过 JSONiq 管理一种方式。如果您需要强大的 JSON 查询,这绝对是您的必经之路。显然,这已与Rumble集成在 Apache Spark 中

无论如何,这是我的解决方案:

jsoniq version "1.0";

let $categories := 
{
  "category_1": [
    {
      "name": "medium",
      "count": 10
    },
    {
      "name": "high",
      "count": 20
    }
  ],
  "category_2": [
    {
      "name": "medium",
      "count": 30
    },
    {
      "name": "high",
      "count": 40
    }
  ]
}

for $key in keys($categories), $row in flatten($categories.$key)
    return {"count": $row.count, "name": $row.name, "category": $key}

输出:

{ "count" : 10, "name" : "medium", "category" : "category_1" }{ "count" : 20, "name" : "high", "category" : "category_1" }{ "count" : 30, "name" : "medium", "category" : "category_2" }{ "count" : 40, "name" : "high", "category" : "category_2" }

你可以在这里试试 Zorba 。

于 2020-05-20T18:07:21.353 回答
0

你实际上不需要任何额外的库。这是一个可以解决问题的小功能。您只需要拆分密钥。

const transform = (obj) => {
    const ret = [];
    for (let key in obj) {
        const tmp = key.split('_');
        for (let item of obj[key]) {
            ret.push({
                ...item,
                [tmp[0]]: tmp[1],
            });
        }
    }
    return ret;
};

const result = transform(obj);
于 2020-05-21T08:05:51.790 回答
0

这是 JSONiq 中的另一种可能性,它不会使用合并构造函数显式列出每一行中的键{| |}

jsoniq version "1.0";

let $categories := 
{
  "category_1": [
    {
      "name": "medium",
      "count": 10
    },
    {
      "name": "high",
      "count": 20
    }
  ],
  "category_2": [
    {
      "name": "medium",
      "count": 30
    },
    {
      "name": "high",
      "count": 40
    }
  ]
}
for $key in keys($categories),
    $row in members($categories.$key)
return {|
  $row,
  { "category": $key }
|}

为了完整起见,这是将输出转换回原始输入(使用 group by 子句)的反向查询:

jsoniq version "1.0";
let $output :=
(
  { "count" : 10, "name" : "medium", "category" : "category_1" },
  { "count" : 20, "name" : "high", "category" : "category_1" },
  { "count" : 30, "name" : "medium", "category" : "category_2" },
  { "count" : 40, "name" : "high", "category" : "category_2" }
)
return
{|
  for $row in $output
  group by $category := $row.category
  return { $category : [ $row ] }
|}
于 2020-05-22T06:11:12.047 回答