用 jq 在 JSON 结构中更深地转换键名

Transforming the name of key deeper in the JSON structure with jq

我关注json:

{
  "vertices": [
    {
      "__cp": "foo",
      "__type": "metric",
      "__eid": "foobar",
      "name": "Undertow Metrics~Sessions Created",
      "_id": 45056,
      "_type": "vertex"
    },
    ...
  ]
  "edges": [
  ...

我想实现这种格式:

{
  "nodes": [
    {
      "cp": "foo",
      "type": "metric",
      "label": "metric: Undertow Metrics~Sessions Created",
      "name": "Undertow Metrics~Sessions Created",
      "id": 45056
    },
    ...
  ]
  "edges": [
  ...

到目前为止我能够创建这个表达式:

jq '{nodes: .vertices} | del(.nodes[]."_type", .nodes[]."__eid")'

即将 'vertices' 重命名为 'nodes' 并删除 '_type' 和 '__eid',如何重命名嵌套在 JSON 中的键?

如果使用 with_entries(filter),您可以更改对象的属性名称。这会将对象转换为 key/value 对的数组,并对这些对应用过滤器并转换回对象。因此,您只想将这些对象的键更新为您的新名称。

根据您使用的 jq 版本,下一部分可能会很棘手。直到 jq 1.5 才引入字符串替换。如果可用,您可以这样做:

{
    nodes: .vertices | map(with_entries(
        .key |= sub("^_+"; "")
    )),
    edges
}

否则,如果您使用的是 jq 1.4,则必须手动删除它们。递归函数可以帮助解决这个问题,因为下划线的数量不同。

def ltrimall(str): str as $str |
    if startswith($str)
        then ltrimstr($str) | ltrimall(str)
        else .
    end;
{
    nodes: .vertices | map(with_entries(
        .key |= ltrimall("_")
    )),
    edges
}

以下程序适用于 jq 1.4 或 jq 1.5。 它使用 walk/1 从任何键中删除前导下划线,无论它出现在输入 JSON.

中的什么位置

此处提供的 ltrim 版本使用 recurse/1 以提高效率和可移植性,但也可以使用任何合适的替代品。

def ltrim(c):
  reduce recurse( if .[0:1] == c then .[1:] else null end) as $x 
    (null; $x);

# Apply f to composite entities recursively, and to atoms
def walk(f):
 . as $in
 | if type == "object" then
      reduce keys[] as $key
        ( {}; . + { ($key):  ($in[$key] | walk(f)) } ) | f
  elif type == "array" then map( walk(f) ) | f
  else f
  end;

.vertices = .nodes
| del(.nodes)
| (.vertices |= walk(
      if type == "object"
      then with_entries( .key |= ltrim("_") )
      else .
      end ))

从你的示例数据来看,你似乎打算进行很多小操作,所以我会把事情分成这样的阶段:

  .nodes = .vertices                     # \ first take care of renaming
| del(.vertices)                         # / .vertices to .nodes

| .nodes = [ 
       .nodes[]                          # \ then scan each node
     | . as $n                           # /

     | del(._type, .__eid)               # \ whatever key-specific tweaks like 
     | .label = "metric: \(.name)"       # / calculating .label you want can go here

     | reduce keys[] as $k (             # \
         {};                             # | final reduce to handle renaming
         .[$k | sub("^_+";"")] = $n[$k]  # | any keys that start with _
       )                                 # /
  ]