2015-09-12 11 views
11

Dati i seguenti JSON:query json che restituisce l'elemento padre e i dati figlio?

{ 
    "README.rst": { 
     "_status": { 
      "md5": "952ee56fa6ce36c752117e79cc381df8" 
     } 
    }, 
    "docs/conf.py": { 
     "_status": { 
      "md5": "6e9c7d805a1d33f0719b14fe28554ab1" 
     } 
    } 
} 

c'è un linguaggio di query che può produrre:

{ 
    "README.rst": "952ee56fa6ce36c752117e79cc381df8", 
    "docs/conf.py": "6e9c7d805a1d33f0719b14fe28554ab1", 
} 

mio miglior tentativo finora con JMESPath (http://jmespath.org/) non è molto vicino:

>>> jmespath.search('*.*.md5[]', db) 
['952ee56fa6ce36c752117e79cc381df8', '6e9c7d805a1d33f0719b14fe28554ab1'] 

Sono arrivato allo stesso punto con ObjectPath (http://objectpath.org):

>>> t = Tree(db) 
>>> list(t.execute('$..md5')) 
['952ee56fa6ce36c752117e79cc381df8', '6e9c7d805a1d33f0719b14fe28554ab1'] 

non ho potuto dare un senso a JSONiq (ne ho veramente bisogno di leggere un manuale di 105 pagina di fare questo?) Questa è la mia prima volta guardando linguaggi di query JSON ..

+0

quindi potresti cercare una chiave diversa da md5? e può essere arbitrariamente profondo? –

+0

corretto – thebjorn

+0

Questo accade nel contesto di un'applicazione Django? Questo può essere fatto tramite i serializzatori rest_framework, ma questo è eccessivo rispetto a quel contesto. – moorecm

risposta

2

Mancato il requisito Python, ma se si è disposti a chiamare il programma esterno, questo funzionerà ancora. Si noti che per funzionare è necessario jq> = 1.5.

# If single "key" $p[0] has multiple md5 keys, this will reduce the array to one key. 
cat /tmp/test.json | \ 
jq-1.5 '[paths(has("md5")?) as $p | { ($p[0]): getpath($p)["md5"]}] | add ' 

# this will not create single object, but you'll see all key, md5 combinations 
cat /tmp/test.json | \ 
jq-1.5 '[paths(has("md5")?) as $p | { ($p[0]): getpath($p)["md5"]}] ' 

Ottenere percorsi con "md5" -key '?' = Ignorare errori (come test di scalare per chiave). Dai percorsi risultanti ($ p) filtrano e circondano il risultato con '{}' = oggetto. E poi quelli sono in un array ([] che circonda l'intera espressione), che è poi "ha aggiunto/fuse" insieme |add

https://stedolan.github.io/jq/

+0

Questo sembra interessante, ma non credo che la tua domanda sia corretta ... sembra che trovi tutte le chiavi foglia (non solo md5) e se ci sono due chiavi foglia sceglierà il valore dell'ultima .. – thebjorn

+0

Questo è il più vicino che ho ottenuto (anche sbagliato ..) '[[percorsi (..) | selezionare (index ("md5"))] come $ p | {(($ p []) [0]): getpath ($ p [])}] | add'. – thebjorn

+0

@thebjorn Hai ragione. Il primo codice ha avuto un "blocco del cervello e la selezione non ha fatto nulla." Ho ora modificato e testato con un file di test più complesso.Non sono davvero un esperto di "jq", ma è stato uno strumento davvero utile. – Manwe

6

non so perché si vuole un linguaggio di query questo è abbastanza facile

def find_key(data,key="md5"): 
    for k,v in data.items(): 
     if k== key: return v 
     if isinstance(v,dict): 
      result = find_key(v,key) 
      if result:return result 

dict((k,find_key(v,"md5")) for k,v in json_result.items()) 

è ancora più facile se il valore dict ha sempre "_status" e "md5" come chiavi

dict((k,v["_status"]["md5"]) for k,v in json_result.items()) 

alternativamente I pensa che si potrebbe fare qualcosa di simile

t = Tree(db) 
>>> dict(zip(t.execute("$."),t.execute('$..md5')) 

anche se Non so che li avrebbe abbinare perfettamente ragione ...

+0

Desidero un linguaggio di query in modo da poter offrire agli utenti finali funzionalità di ricerca ad hoc in un file .json molto grande. – thebjorn

+1

crea un "find_md5.py" e hai una soluzione ad-hoc ... –

+0

Penso che potresti voler cercare ad hoc ;-) Ogni percorso di file ha un certo numero di "categorie" come figli diretti (solo ' _status' in questo esempio) e ogni "categoria" contiene dati (potenzialmente profondamente annidati). Vorrei creare uno strumento a riga di comando che dia agli utenti finali (sviluppatori) un modo per interrogare questa struttura (si pensi grep strutturato). – thebjorn

2

Una soluzione che implementa un nuovo linguaggio di query:

def keylist(db): 
    "Return all the keys in db." 

    def _keylist(db, prefix, res): 
     if prefix is None: 
      prefix = [] 

     for key, val in db.items(): 
      if isinstance(val, dict): 
       _keylist(val, prefix + [key], res) 
      else: 
       res.append(prefix + [key]) 

    res = [] 
    _keylist(db, [], res) 
    return ['::'.join(key) for key in res] 

def get_key(db, key): 
    "Get path and value from key." 

    def _get_key(db, key, path): 
     k = key[0] 
     if len(key) == 1: 
      return path + [k, db[k]] 
     return _get_key(db[k], key[1:], path + [k]) 

    return _get_key(db, key, []) 

def search(query, db): 
    "Convert query to regex and use it to search key space." 
    keys = keylist(db) 
    query = query.replace('*', r'(?:.*?)') 
    matching = [key for key in keys if re.match(query, key)] 
    res = [get_key(db, key.split('::')) for key in matching] 
    return dict(('::'.join(r[:-1]), r[-1]) for r in res) 

che dà qualcosa che è abbastanza vicino ai requisiti:

>>> pprint.pprint(search("*::md5", db)) 
{'README.rst::_status::md5': '952ee56fa6ce36c752117e79cc381df8', 
'docs/conf.py::_status::md5': '6e9c7d805a1d33f0719b14fe28554ab1'} 

e un linguaggio di query che sembra s come un glob/re ibrido (se stiamo facendo un nuovo linguaggio, almeno farlo sembrare familiare):

>>> pprint.pprint(search("docs*::md5", db)) 
{'docs/conf.py::_status::md5': '6e9c7d805a1d33f0719b14fe28554ab1'} 

poiché i dati contiene percorsi di file che ho usato in modo casuale :: come un separatore di percorso. (Sono abbastanza sicuro che non gestisca ancora la grammatica completa di JSON, ma che dovrebbe essere per lo più un lavoro da grugnito).

+0

Penso che sia un po 'strano che tu non voglia implementare un nuovo linguaggio di query (almeno in parte perché eri preoccupato per i bug ...) ma poi usi un algoritmo molto più complesso di quello che stavo suggerendo (un po' più complesso) ... che ha detto che se risolve il tuo problema sono lieto :) +1 per una risposta che risolva il tuo problema –

+0

Concettualmente è semplice, trasforma il problema in una forma in cui il modulo re può essere utilizzato per risolvere il problema di ricerca. La serializzazione dello spazio-chiave e il recupero di un percorso ad albero sono entrambi test di scrittura relativamente facili, quindi sono relativamente tranquillo con i problemi di qualità del codice. È un linguaggio di query molto limitato, non dello stesso livello di ObjectPath o JMESPath, e come soluzione_Soluzione_ Penso che sia inaccettabile (perché qualcuno dovrebbe voler imparare il mio linguaggio di query molto limitato?) Imposterò una taglia su non appena sono in grado di vedere se ci sono soluzioni usando gli strumenti standard .. – thebjorn

+0

una domanda migliore potrebbe essere perché un __developer__ desidera imparare un linguaggio di query più complicato come JMES quando circa 4 linee di python relativamente semplice sarebbero sufficienti .... a meno che quando dici per gli sviluppatori non intendi lo sviluppatore del software .. –

1

Se il JSON è ben strutturato, vale a dire. assicurato che avrai i sottoelementi _status e md5, potresti semplicemente caricare il json e usare una comprensione di lista per sputare gli oggetti che stai cercando.

>>> import json 
>>> my_json = json.loads(json_string) 
>>> print [(key, value['_status']['md5']) for key, value in my_json.iteritems()] 
[(u'README.rst', u'952ee56fa6ce36c752117e79cc381df8'), (u'docs/conf.py', u'6e9c7d805a1d33f0719b14fe28554ab1')] 
+0

Questa è la stessa soluzione di Joran Beasley (a partire da "è ancora più semplice ..") – thebjorn

4

Ecco il codice JSONiq che fa il lavoro:

{| 
    for $key in keys($document) 
    return { 
     $key: $document.$key._status.md5 
    } 
|} 

È possibile eseguirlo here con il motore di Zorba.

Se il manuale di 105 pagine di cui si parla è la specifica, non è consigliabile leggerlo come utente JSONiq. Preferirei consigliare di leggere tutorial o libri online, che offrono un'introduzione più gentile.

+0

Grazie! Puoi suggerire qualche tutorial che va un po 'in -depth? – thebjorn

+0

Ciao thebjorn, certo! Ho rianimato un vecchio tutorial [qui] (https://github.com/ghislainfourny/jsoniq-tutorial) e l'ho aggiornato alla più recente sintassi. Inoltre, ci sono un paio di discorsi online su youtube di Jonathan Robie e Chris Hillery. –

2

Do in ObjectPath:

l = op.execute("[keys($.*), $..md5]") 

si otterrà:

[ 
    [ 
    "README.rst", 
    "docs/conf.py" 
    ], 
    [ 
    "952ee56fa6ce36c752117e79cc381df8", 
    "6e9c7d805a1d33f0719b14fe28554ab1" 
    ] 
] 

poi in Python:

dict(zip(l[0],l[1])) 

per ottenere:

{ 
    'README.rst': '952ee56fa6ce36c752117e79cc381df8', 
    'docs/conf.py': '6e9c7d805a1d33f0719b14fe28554ab1' 
} 

Spero che questo aiuti. :)

PS. Sto usando i tasti OP() per mostrare come creare un'interrogazione completa che funzioni in qualsiasi parte del documento, non solo quando le chiavi sono nella radice del documento.

PS2. Potrei aggiungere una nuova funzione in modo che assomigli a: object ([keys ($. *), $ .. md5]). Sparami tweet http://twitter.com/adriankal se lo vuoi.

Problemi correlati