2017-10-29 1 views
3

sagen, wir haben die folgende JSON:Wie mache ich Wege zu Blättern eines JSON?

[ 
    { 
    "dir-1": [ 
     "file-1.1", 
     "file-1.2" 
    ] 
    }, 
    "dir-1", 
    { 
    "dir-2": [ 
     "file-2.1" 
    ] 
    } 
] 

Und wollen wir die nächste Ausgabe erhalten:

"dir-1/file-1.1" 
    "dir-1/file-1.2" 
    "dir-1" 
    "dir-2/file-2.1" 

das heißt die Pfade zu allen Blättern zu erhalten, Gegenstände mit / verbinden. Gibt es eine Möglichkeit, das auf JQ zu tun?

Ich habe versucht, so etwas wie dieses:

cat source-file | jq 'path(..) | [ .[] | tostring ] | join("/")' 

Aber es produziert nicht, was ich einmal in der Nähe brauchen.

Antwort

3

Sie könnten die Funktionsweise von Streams nutzen, indem Sie den Pfad mit ihren Werten kombinieren. Streams geben nur path, value Paare für Blattwerte aus. Ignorieren Sie einfach die nummerierten Indizes.

$ jq --stream ' 
select(length == 2) | [(.[0][] | select(strings)), .[1]] | join("/") 
' source-file 

kehrt:

"dir-1/file-1.1" 
"dir-1/file-1.2" 
"dir-1" 
"dir-2/file-2.1" 
+0

Sie sind Genie! Vielen Dank. – Onkeltem

0

Hier ist eine Lösung ähnlich wie Jeff Mercado ‚s die tostream und flatten

tostream | select(length==2) | .[0] |= map(strings) | flatten | join("/") 

Try it online at jqplay.org

Eine andere Möglichkeit nutzt, ist eine rekursive Funktion zu verwenden, die Eingabe wie

def slashpaths($p): 
    def concat($p;$k): if $p=="" then $k else "\($p)/\($k)" end; 
    if type=="array" then .[] | slashpaths($p) 
    elif type=="object" then 
     keys_unsorted[] as $k 
    | .[$k] | slashpaths(concat($p;$k)) 
    else concat($p;.) end; 
slashpaths("") 

Try it online at tio.run!

+0

Was ist die Leistung der rekursiven Funktion? Ein Teil des Grundes, warum ich es aufgenommen habe, war, weil ich dachte, dass die Bottom-Up-Konstruktion 'Tostream' prohibitiv sein könnte. – jq170727

+1

Die Lösung, die 'tostream' verwendet, ist viel langsamer als alle anderen (d. H. Diejenige, die' -stream' verwendet, den rekursiven walk-like oder den direkten, der auf 'paths' basiert). Dies liegt daran, dass "tostream" sowohl die Kosten für das Lesen der gesamten Datei als auch die Kosten für das Streaming der Inhalte verursacht. Für eine große Datei waren die u + s Zeiten jeweils: 'tostream': 38s,' slashpaths': 10,4, '--stream': 15,5s,' Pfade': 22,4s. – peak

+0

Vielen Dank für die Aufschlüsselung. Das ist gut zu wissen. 'tostream' ist oft sehr praktisch, aber es kann auch ineffizient sein, denke ich teilweise aufgrund der Anzahl von Zwischenobjekten, die es erzeugt. Jeder Pfad, den es zurückgibt, beginnt am Blatt ['[[], $ dot]'] (https://github.com/stedolan/jq/blob/master/src/builtin.jq#L236) und jeder Interior-Schlüssel wird vorangestellt ['. [0]'] (https://github.com/stedolan/jq/blob/master/src/builtin.jq#L246). Wenn viele dieser Pfade später verworfen werden (was häufig der Fall ist, wenn sie gefiltert oder aggregiert werden), ist das eine Menge zusätzlicher Arbeit. – jq170727

0

--stream zu verwenden ist gut, aber die folgende ist vielleicht weniger esoterisch gehen:

paths(scalars) as $p 
| getpath($p) as $v 
| ($p | map(strings) + [$v]) 
| join("/") 

(Bei Verwendung von jq 1.4 oder früher, und wenn eine der Blätter können numerisch oder boolesch oder Null sein, dann oben sollte durch [$v|tostring] ersetzt werden.)

Ob die Ergebnis sollte als "Wege zu Blättern" betrachtet werden ist eine andere Sache ...

Verwandte Themen