使用 jq 展平/标准化 json 对象数组
Flatten / normalize json array of objects with jq
我有一大堆 json 对象。每个对象包含一个 foreignKeyId
、一个 url
、(可选)一个 urlMirror1
和(可选)一个 urlMirror2
.
这是一个示例:
[
{
"foreignKeyId": 1,
"url": "https://1-url.com"
},
{
"foreignKeyId": 2,
"url": "https://2-url.com",
"urlMirror1": "https://2-url-mirror-1.com",
},
{
"foreignKeyId": 3,
"url": "https://3-url.com",
"urlMirror1": "https://3-url-mirror-1.com",
"urlMirror2": "https://3-url-mirror-2.com"
}
}
我想将此 json 规范化为如下所示:
[
{
"foreignKeyId": 1,
"primariness": 1,
"url": "https://1-url.com"
},
{
"foreignKeyId": 2,
"primariness": 1,
"url": "https://2-url.com",
},
{
"foreignKeyId": 2,
"primariness": 2,
"url": "https://2-url-mirror-1.com",
},
{
"foreignKeyId": 3,
"primariness": 1,
"url": "https://3-url.com"
},
{
"foreignKeyId": 3,
"primariness": 2,
"url": "https://3-url-mirror-1.com",
},
{
"foreignKeyId": 3,
"primariness": 3,
"url": "https://3-url-mirror-2.com"
}
}
有没有办法使用 jq
做这样的事情?如果没有,还有其他建议可以在不编写太多自定义代码的情况下快速完成此操作吗?这只需要 运行 一次,因此任何一种 hacky 一次性解决方案都可以工作(bash 脚本等)。
谢谢!
更新:
primariness
应该从键名派生 (url
=> 1
, urlMirror1
=> 2
, urlMirror2
=> 3
. 任何给定对象内的键的顺序都是无关紧要的。镜像的数量是固定的(例如,永远不会有 urlMirror3
)。
这是一个简单的脚本,其中硬编码了镜像数量和主要性。希望它能解决问题。
jq '
map(
{ foreinKeyId } +
(
{ primariness: 1, url },
(.urlMirror1 // empty | { primariness: 2, url: . }),
(.urlMirror2 // empty | { primariness: 3, url: . })
)
)
' input.json
鉴于 OP 已将查询从通用限制为更具体的标准,@luciole75w 提供的答案是最好的(最有可能),请参考那个。
现在,对于@oguzismail,这是一个通用的 jtc
方法(它将处理任意数量的 "urlMirror"s
),由 3 JSON 个转换步骤(更新的解决方案):
<file.json jtc -w'<foreignKeyId>l:<f>v[-1]<urlM>L:<u>v[^0]' \
-i'{"url":{{u}},"foreignKeyId":{f}}' /\
-w'[foreignKeyId]:<f>q:<p:0>v[^0][foreignKeyId]:<f>s:[-1]<p>I1' \
-i'{"primeriness":{{p}}}' /\
-pw'<urlM>L:' -tc
[
{ "foreignKeyId": 1, "primeriness": 1, "url": "https://1-url.com" },
{ "foreignKeyId": 2, "primeriness": 1, "url": "https://2-url.com" },
{ "foreignKeyId": 3, "primeriness": 1, "url": "https://3-url.com" },
{ "foreignKeyId": 2, "primeriness": 2, "url": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 2, "url": "https://3-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 3, "url": "https://3-url-mirror-2.com" }
]
bash $
解释和可视化:
- 所有 3 个步骤都可以在 "slow-mo":
中观察到
1. 对于在同一记录中找到的每个 "foreignKeyId"
和每个 "urlMirror"
扩展(插入)数组 {"url":... , "foreignKeyId": ...}
:
<file.json jtc -w'<foreignKeyId>l:<f>v[-1]<urlM>L:<u>v[^0]' \
-i'{"url":{{u}},"foreignKeyId":{f}}' -tc
[
{ "foreignKeyId": 1, "url": "https://1-url.com" },
{ "foreignKeyId": 2, "url": "https://2-url.com", "urlMirror1": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "url": "https://3-url.com", "urlMirror1": "https://3-url-mirror-1.com", "urlMirror2": "https://3-url-mirror-2.com" },
{ "foreignKeyId": 2, "url": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "url": "https://3-url-mirror-1.com" },
{ "foreignKeyId": 3, "url": "https://3-url-mirror-2.com" }
]
bash $
2。现在根据 foreignKeyId
:
出现的索引插入 "primariness": N
记录
<file.json jtc -w'<foreignKeyId>l:<f>v[-1]<urlM>L:<u>v[^0]' \
-i'{"url":{{u}},"foreignKeyId":{f}}' /\
-w'[foreignKeyId]:<f>q:<p:0>v[^0][foreignKeyId]:<f>s:[-1]<p>I1' \
-i'{"primeriness":{{p}}}' -tc
[
{ "foreignKeyId": 1, "primeriness": 1, "url": "https://1-url.com" },
{ "foreignKeyId": 2, "primeriness": 1, "url": "https://2-url.com", "urlMirror1": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 1, "url": "https://3-url.com", "urlMirror1": "https://3-url-mirror-1.com", "urlMirror2": "https://3-url-mirror-2.com" },
{ "foreignKeyId": 2, "primeriness": 2, "url": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 2, "url": "https://3-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 3, "url": "https://3-url-mirror-2.com" }
]
bash $
3。最后一步 (-pw'<urlM>L:'
) - 删除所有多余的 "urlMirror"
s 记录。
可选:如果需要按照 OP 的示例对顶部数组中的所有记录进行排序,则此附加步骤将执行:-jw'[foreignKeyId]:<>g:[-1]'
PS。碰巧我也是 jtc
unix 工具
的开发者
这是一个通用的解决方案,即它会处理任意多个url镜像。
为了清楚起见,让我们首先定义一个辅助函数,它为单个输入对象发出 {foreignKeyId, primariness, url} 对象流:
def primarinesses:
{foreinKeyId} +
({primariness:1, url},
(to_entries[]
| (.key | capture( "^urlMirror(?<n>[0-9]+)")) as $n
| {primariness: ($n.n | tonumber + 1), url : .value } )) ;
解决方法很简单:
[.[] | primarinesses]
少标点也可以写成:
map(primarinesses)
我有一大堆 json 对象。每个对象包含一个 foreignKeyId
、一个 url
、(可选)一个 urlMirror1
和(可选)一个 urlMirror2
.
这是一个示例:
[
{
"foreignKeyId": 1,
"url": "https://1-url.com"
},
{
"foreignKeyId": 2,
"url": "https://2-url.com",
"urlMirror1": "https://2-url-mirror-1.com",
},
{
"foreignKeyId": 3,
"url": "https://3-url.com",
"urlMirror1": "https://3-url-mirror-1.com",
"urlMirror2": "https://3-url-mirror-2.com"
}
}
我想将此 json 规范化为如下所示:
[
{
"foreignKeyId": 1,
"primariness": 1,
"url": "https://1-url.com"
},
{
"foreignKeyId": 2,
"primariness": 1,
"url": "https://2-url.com",
},
{
"foreignKeyId": 2,
"primariness": 2,
"url": "https://2-url-mirror-1.com",
},
{
"foreignKeyId": 3,
"primariness": 1,
"url": "https://3-url.com"
},
{
"foreignKeyId": 3,
"primariness": 2,
"url": "https://3-url-mirror-1.com",
},
{
"foreignKeyId": 3,
"primariness": 3,
"url": "https://3-url-mirror-2.com"
}
}
有没有办法使用 jq
做这样的事情?如果没有,还有其他建议可以在不编写太多自定义代码的情况下快速完成此操作吗?这只需要 运行 一次,因此任何一种 hacky 一次性解决方案都可以工作(bash 脚本等)。
谢谢!
更新:
primariness
应该从键名派生 (url
=> 1
, urlMirror1
=> 2
, urlMirror2
=> 3
. 任何给定对象内的键的顺序都是无关紧要的。镜像的数量是固定的(例如,永远不会有 urlMirror3
)。
这是一个简单的脚本,其中硬编码了镜像数量和主要性。希望它能解决问题。
jq '
map(
{ foreinKeyId } +
(
{ primariness: 1, url },
(.urlMirror1 // empty | { primariness: 2, url: . }),
(.urlMirror2 // empty | { primariness: 3, url: . })
)
)
' input.json
鉴于 OP 已将查询从通用限制为更具体的标准,@luciole75w 提供的答案是最好的(最有可能),请参考那个。
现在,对于@oguzismail,这是一个通用的 jtc
方法(它将处理任意数量的 "urlMirror"s
),由 3 JSON 个转换步骤(更新的解决方案):
<file.json jtc -w'<foreignKeyId>l:<f>v[-1]<urlM>L:<u>v[^0]' \
-i'{"url":{{u}},"foreignKeyId":{f}}' /\
-w'[foreignKeyId]:<f>q:<p:0>v[^0][foreignKeyId]:<f>s:[-1]<p>I1' \
-i'{"primeriness":{{p}}}' /\
-pw'<urlM>L:' -tc
[
{ "foreignKeyId": 1, "primeriness": 1, "url": "https://1-url.com" },
{ "foreignKeyId": 2, "primeriness": 1, "url": "https://2-url.com" },
{ "foreignKeyId": 3, "primeriness": 1, "url": "https://3-url.com" },
{ "foreignKeyId": 2, "primeriness": 2, "url": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 2, "url": "https://3-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 3, "url": "https://3-url-mirror-2.com" }
]
bash $
解释和可视化:
- 所有 3 个步骤都可以在 "slow-mo":
中观察到
1. 对于在同一记录中找到的每个 "foreignKeyId"
和每个 "urlMirror"
扩展(插入)数组 {"url":... , "foreignKeyId": ...}
:
<file.json jtc -w'<foreignKeyId>l:<f>v[-1]<urlM>L:<u>v[^0]' \
-i'{"url":{{u}},"foreignKeyId":{f}}' -tc
[
{ "foreignKeyId": 1, "url": "https://1-url.com" },
{ "foreignKeyId": 2, "url": "https://2-url.com", "urlMirror1": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "url": "https://3-url.com", "urlMirror1": "https://3-url-mirror-1.com", "urlMirror2": "https://3-url-mirror-2.com" },
{ "foreignKeyId": 2, "url": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "url": "https://3-url-mirror-1.com" },
{ "foreignKeyId": 3, "url": "https://3-url-mirror-2.com" }
]
bash $
2。现在根据 foreignKeyId
:
"primariness": N
记录
<file.json jtc -w'<foreignKeyId>l:<f>v[-1]<urlM>L:<u>v[^0]' \
-i'{"url":{{u}},"foreignKeyId":{f}}' /\
-w'[foreignKeyId]:<f>q:<p:0>v[^0][foreignKeyId]:<f>s:[-1]<p>I1' \
-i'{"primeriness":{{p}}}' -tc
[
{ "foreignKeyId": 1, "primeriness": 1, "url": "https://1-url.com" },
{ "foreignKeyId": 2, "primeriness": 1, "url": "https://2-url.com", "urlMirror1": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 1, "url": "https://3-url.com", "urlMirror1": "https://3-url-mirror-1.com", "urlMirror2": "https://3-url-mirror-2.com" },
{ "foreignKeyId": 2, "primeriness": 2, "url": "https://2-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 2, "url": "https://3-url-mirror-1.com" },
{ "foreignKeyId": 3, "primeriness": 3, "url": "https://3-url-mirror-2.com" }
]
bash $
3。最后一步 (-pw'<urlM>L:'
) - 删除所有多余的 "urlMirror"
s 记录。
可选:如果需要按照 OP 的示例对顶部数组中的所有记录进行排序,则此附加步骤将执行:-jw'[foreignKeyId]:<>g:[-1]'
PS。碰巧我也是 jtc
unix 工具
这是一个通用的解决方案,即它会处理任意多个url镜像。
为了清楚起见,让我们首先定义一个辅助函数,它为单个输入对象发出 {foreignKeyId, primariness, url} 对象流:
def primarinesses:
{foreinKeyId} +
({primariness:1, url},
(to_entries[]
| (.key | capture( "^urlMirror(?<n>[0-9]+)")) as $n
| {primariness: ($n.n | tonumber + 1), url : .value } )) ;
解决方法很简单:
[.[] | primarinesses]
少标点也可以写成:
map(primarinesses)