Как отфильтровать JSON на основе списка путей в JQ
Учитывая произвольный ввод JSON:
{
"id":"038020",
"title":"Teenage Mutant Ninja Turtles: Out of the Shadows",
"turtles":[
{
"name":"Leonardo",
"mask":"blue"
},
{
"name":"Michelangelo",
"mask":"orange"
},
{
"name":"Donatello",
"mask":"purple"
},
{
"name":"Raphael",
"mask":"red"
}
],
"summary":"The Turtles continue to live in the shadows and no one knows they were the ones who took down Shredder",
"cast":"Megan Fox, Will Arnett, Tyler Perry",
"director":"Dave Green"
}
И произвольный список путей JQ, таких как [".turtles[].name", ".cast", ".does.not.exist"]
или любой подобный формат
Как я могу создать новый JSON только с информацией, содержащейся в путях списка? В этом случае ожидаемый результат будет:
{
"turtles":[
{
"name":"Leonardo"
},
{
"name":"Michelangelo"
},
{
"name":"Donatello"
},
{
"name":"Raphael"
}
],
"cast":"Megan Fox, Will Arnett, Tyler Perry"
}
Я видел похожие решения в таких проблемах, как "удаление null
записи " из JSON с использованием функции ходьбы, представленной в jq1.5 +, примерно так:
def filter_list(input, list):
input
| walk(
if type == "object" then
with_entries( select(.key | IN( list )))
else
.
end);
filter_list([.], [.a, .b, .c[].d])
Но это должно как-то учитывать полный путь в JSON.
Каков наилучший подход к решению этой проблемы?
1 ответ
Если $paths содержит массив явных путей jq (таких как [ ["turtles", 0, "name"], ["cast"]])
Самый простой подход - использовать следующий фильтр:
. as $in
| reduce $paths[] as $p (null; setpath($p; $in | getpath($p)))
Расширенные выражения пути
Для того, чтобы иметь возможность обрабатывать расширенные выражения пути, такие как ["turtles", [], "name"], где []
предназначен для охвата индексов turtles
массив, мы определим следующую вспомогательную функцию:
def xpath($ary):
. as $in
| if ($ary|length) == 0 then null
else $ary[0] as $k
| if $k == []
then range(0;length) as $i | $in[$i] | xpath($ary[1:]) | [$i] + .
else .[$k] | xpath($ary[1:]) | [$k] + .
end
end ;
Для изложения давайте также определим:
def paths($ary): $ary[] as $path | xpath($path);
Затем с заданным вводом выражения:
. as $in
| reduce paths([ ["turtles", [], "name"], ["cast"]]) as $p
(null; setpath($p; $in | getpath($p)) )
производит вывод, показанный ниже.
С помощью path
Стоит отметить, что одним из способов обработки таких выражений, как ".turtles[]. Name", было бы использование встроенного фильтра. path/1
,
Например:
# Emit a stream of paths:
def paths: path(.turtles[].name), ["cast"];
. as $in
| reduce paths as $p (null; setpath($p; $in | getpath($p)))
Выход:
{
"turtles": [
{
"name": "Leonardo"
},
{
"name": "Michelangelo"
},
{
"name": "Donatello"
},
{
"name": "Raphael"
}
],
"cast": "Megan Fox, Will Arnett, Tyler Perry"
}