Как отфильтровать JSON на основе списка путей в JQ - PullRequest
0 голосов
/ 31 мая 2018

С учетом произвольного ввода 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 с использованием walk функция присутствует в 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 Ответ

0 голосов
/ 31 мая 2018

Если $ paths содержит массив явных jq-путей (например, [ ["turtles", 0, "name"], ["cast"]]), самый простой подход - использовать следующий фильтр:

. as $in
| reduce $paths[] as $p (null; setpath($p; $in | getpath($p)))

Расширенные выражения пути

InДля того, чтобы иметь возможность обрабатывать расширенные выражения пути, такие как ["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"
}
...