opendatasicilia / tansignari

"T'ansignari e t'appeddiri"
http://tansignari.opendatasicilia.it
Creative Commons Attribution 4.0 International
18 stars 10 forks source link

[json] estrarre dati, non cerco script ma ragionamenti utili per estrarre i dati #251

Open pigreco opened 1 year ago

pigreco commented 1 year ago

Ho questo file json (relazioni)

image

il mio interesse è nell'estrarre i dati presenti nei vari oggetti con struttura diversa e creare file csv:

  1. layers
  2. metadati
  3. domains
  4. rootGroup
  5. relationships.

per esempio layers ha questa struttura:

image

in questo caso come estrarre, per ogni layer, tutte le sue proprietà e renderle disponibili in formato csv?

domains ha questa struttura:

image

in questo caso come estrarre, per ogni layer, tutte le sue proprietà e renderle disponibili in formato csv?

relationships ha questa struttura:

image

in questo caso come estrarre, per ogni layer, tutte le sue proprietà e renderle disponibili in formato csv?

a me interessa estrarre, per ogni keys, tutti i suoi oggetti e allinearli in un csv; sotto un esempio di output

relationships type related_table_type cardinality left_table_name right_table_name left_table_fields right_table_fields
REL_AB_CDA_AB_CDA_AB_CDA_SUP Association feature OneToOne AB_CDA AB_CDA_AB_CDA_SUP ClassID ClassREF
REL_AB_CDA_AB_CDA_AB_CDA_SUP Association feature OneToMany AB_CDA AB_CDA_AB_CDA_SUP ClassID ClassREF

relazioni.zip

aborruso commented 1 year ago

Caro @pigreco, non riesco a fare "spiegoni". Ma provo a iniziare con qualche esempio.

Il punto di partenza è avere uno strumento per esplorare il file. Va bene un buon editor di testo, capace di mappare la struttura.

Io uso la riga di comando, con gli strumenti di base per il testo (head, tail, grep, ...) e jq e soprattutto (per esplorare) fx.

image

Esplorando il tuo file, si vede che a primo livello gerarchico c'è una proprietà di tuo interesse relationships.

Se la vogliamo estrarre, con la gran parte dei tool, basterà scrivere .relationships, che in qualche modo vuol dire "a partire dalla radice, estrai relationships. In jq quindi è

<relazioni.json jq  '.relationships'

In output l'elenco di tutte le relazioni:

image

Ogni relazione è un a chiave, a cui è associato - dopo i : - un valore. Il valore non è in questo caso una semplice stringa, ma un oggetto JSON, composto da altre coppie chiave valore.

In jq, puoi avere l'elenco delle chiavi di relationships con <relazioni.json jq '.relationships|keys'.

Se vuoi esplorare una specifica key, dell'oggetto relationship, quindi ad esempio la relazione REL_AB_CDA_AB_CDA_AB_CDA_SUP

<relazioni.json jq '.relationships.REL_AB_CDA_AB_CDA_AB_CDA_SUP'
{
  "type": "Association",
  "related_table_type": "feature",
  "cardinality": "OneToOne",
  "left_table_name": "AB_CDA",
  "right_table_name": "AB_CDA_AB_CDA_SUP",
  "left_table_fields": [
    "ClassID"
  ],
  "right_table_fields": [
    "ClassREF"
  ],
  "forward_path_label": "Attributi da AB_CDA_AB_CDA_SUP",
  "backward_path_label": "Attributi da AB_CDA"
}

E dentro questo oggetto trovi coppie chiave valore semplici, e chiave che contengono un array. L'array si riconosce dalle parentesi quadre. Sopra, ad esempio, è right_table_fields. Anche se è un solo valore, è mappato come array, perché potenzialmente può essere composto da più valori.

Se vuoi recuperare il primo valore di array di right_table_fields

<relazioni.json jq '.relationships.REL_AB_CDA_AB_CDA_AB_CDA_SUP.right_table_fields[0]'

Con 0 si indica il primo, perché sono "zero based".

Per estrarre soltanto le relazioni, o altri oggetti, si tratta soltanto di comprendere come può essere fatta la struttura di un json, con oggetti, chiavi, valori, array, ecc. e scegliere uno strumento per fare query di estrazione/trasformazione.

Io spesso faccio tutto (anche per pigrizia) con la coppia jq, miller. Ma l'ideale è fare tutto con uno strumento.

Miller, e molti altri programmi per lettura e trasformazione di dati, preferiscono avere oggetti elaborabili linea per linea. E per questo, anche per ragioni di performance, esiste il formato JSON Lines, in cui ogni linea è un singolo oggetto JSON valido.

Quindi invece di avere un contenitore con N oggetti dentro

image

si estraggono i singoli oggetti, e ogni linea è come se fosse il contenuto di un singolo file JSON.

{"type":"Association","related_table_type":"feature","cardinality":"OneToOne","left_table_name":"AB_CDA","right_table_name":"AB_CDA_AB_CDA_SUP","left_table_fields":["ClassID"],"right_table_fields":["ClassREF"],"forward_path_label":"Attributi da AB_CDA_AB_CDA_SUP","backward_path_label":"Attributi da AB_CDA"}
{"type":"Association","related_table_type":"feature","cardinality":"OneToOne","left_table_name":"AB_CDA","right_table_name":"AB_CDA_AB_CDA_SUP_L","left_table_fields":["ClassID"],"right_table_fields":["ClassREF"],"forward_path_label":"Attributi da AB_CDA_AB_CDA_SUP_L","backward_path_label":"Attributi da AB_CDA"}
{"type":"Association","related_table_type":"feature","cardinality":"OneToOne","left_table_name":"AB_CDA","right_table_name":"AB_CDA_AB_CDA_SUP_SG","left_table_fields":["ClassID"],"right_table_fields":["ClassREF"],"forward_path_label":"Attributi da AB_CDA_AB_CDA_SUP_SG","backward_path_label":"Attributi da AB_CDA"}

Con jq, si ottiene con <relazioni.json jq -c '.relationships[]'. Aggiungendo [] fa in modo di rimuovere il contenuto di relationships dalle {} e con -c hai tutto compattato in singole righe.

Questo formato, è "masticabile" nativamente da Miller, visidata, ecc..

Con visidata è

<relazioni.json jq -c '.relationships[]' | vd -f jsonl

Con Miller

<relazioni.json jq -c '.relationships[]' | mlr --j2c unsparsify 

image

Per il momento mi fermo qui.

pigreco commented 1 year ago

Bellissima spiegazione, unica cosa che non capisco e che se lancio

<relazioni.json jq -c '.relationships[]' | vd -f jsonl

ottengo: image

quindi uso:

<relazioni.json jq -c '.relationships[]' | mlr --j2c unsparsify  | vd -f csv

per ottenere:

image

aborruso commented 1 year ago

Ciao @pigreco Miller e VisiData sono due programmi distinti e ognuno ha delle scelte diverse sui dati di input.

Per i JSON, vd di default non espande le colonne che sono array e che potenzialmente possono avere quindi valori multipli.

Però una volta aperto il flusso JSON, puoi espandere la singola colonna, tutte le colonne, ecc..

Dopo che lo apri in vd, dai ad esempio il comando g( per espandere tutte le colonne (qui la documentazione).

pigreco commented 1 year ago

Aggiungo un po' di teoria:

JSON prende origine dalla sintassi degli oggetti letterali in JavaScript. Un oggetto letterale può essere definito così:

var JSON = {
proprieta1: 'Valore',
proprieta2: 'Valore',
proprietaN: 'Valore'
}

Si tratta di coppie di proprietà/valori separate dalla virgola ad eccezione dell'ultima. L'intero oggetto viene racchiuso tra parentesi graffe. A differenza di questa notazione JavaScript, che può contenere anche funzioni e valori complessi, JSON ammette solo valori semplici ed atomici, tra cui:

link utili:

Cos'è JSON?

Regole della sintassi JSON:

  1. Un JSON object è circondato da parentesi graffe {}.
  2. Le coppie nome-valore sono raggruppate per a colon (:) e separati da a virgola (,).
  3. Un array inizia con a parentesi di sinistra e termina con a parentesi a destra [].
  4. Le virgole finali e gli zeri iniziali in un numero sono vietati.
  5. I formati ottale ed esadecimale non sono ammessi.
  6. Each chiave all'interno del JSON dovrebbe essere univoco e dovrebbe essere racchiuso tra virgolette.
  7. Il boolean type corrisponde solo a due valori speciali: true e false e i valori NULL sono rappresentati da null letterale (senza virgolette).

Struttura di un oggetto JSON:

Un JSON può essere: