È possibile convertire automaticamente un file JSON in RDF?
Le differenze tra questi due modelli di dati va oltre una banale traduzione di formato. Non si tratta infatti di una differente serializzazione, ma di due approcci sostanzialmente diversi.
Grafi vs alberi, URI vs shortnames, literals con lingua e datatype vs stringhe, proprietà di un’ontologia vs semplici valori, blank nodes vs vettori e dizioniari (Can JSON and RDF be friends)
Un approccio custom
Certamente è possibile costruire un sistema personalizzato, scrivendo qualche riga di codice di conversione per ogni campo del JSON di partenza, così, foglia per foglia, “ramo” per “ramo”, gli oggetti diventano triple e l’albero si trasforma in un grafo. Per chi volesse cimentarsi in questo approccio troverà in Jena un valido alleato.
JSON-LD
Tuttavia il W3C ci mostra un’altra via: con la raccomandazione JSON-LD (JSON for Linked Data). Utilizzando il concetto di “contesto”, un JSON-LD non è altro che un JSON con alcuni campi aggiuntivi, che riguardano l’ontologia dei predicati e degli oggetti che troveremmo in un RDF.
{ "@context": { "name": "http://xmlns.com/foaf/0.1/name", "homepage": { "@id": "http://xmlns.com/foaf/0.1/workplaceHomepage", "@type": "@id" }, "Person": "http://xmlns.com/foaf/0.1/Person" }, "@id": "http://mario.example.com", "@type": "Person", "name": "Mario Rossi", "homepage": "http://www.example.com/" }
Nell’esempio, si descrive la persona Mario, che ha come URI http://mario.example.com
, come nome Mario Rossi
, ha un homepage http://www.example.com/
ed è di tipo Person
. Cosa sono homepage
, name
e Person
è descritto nel contesto, ovvero un frammento dell’ontologia foaf.
In un JSON-LD ritroviamo quindi tutti i tasselli che compongono un RDF, serializzati sotto forma di JSON. Una conversione risulta quindi facilitata. Ma là fuori ci sono JSON e non JSON-LD. Quest’ultimo può essere utilizzato come file di mapping tra un JSON di partenza e l’RDF che si desidera in output, per farlo poi processare ad un convertitore da JSON-LD a RDF. Sebbene l’approccio sia elegante, l’effort richiesto è paragonabile a quello di un approccio tramite codice di conversione custom.
Infine venne JARQL
Beh, ad essere onesti prima di lui venne TARQL: SPARQL per Tabelle. L’idea è estremamente ingegnosa: perché imparare nuove sintassi o nuovo codice quando basta usare SPARQL 1.1 per convertire un CSV in RDF? Attraverso l’utilizzo di una CONSTRUCT
si definiscono le triple che si vogliono ottenere nell’RDF finale, con tanto di predicati, utilizzando come nome delle variabili gli header dei campi del CSV. Scrivendo poi nella WHERE
alcuni BIND
, si possono fare tutti i tipi di manipolazione di stringhe, conversione in URI e aggiunta di datatype che SPARQL 1.1 consente.
Come fare la stessa cosa per i JSON?
Usare SPARQL come in TARQL sembra molto intrigante e potente ma quest’ultimo sfrutta la non ramificazione del formato tabellare per individuare le variabili da manipolare. Un JSON, che invece può avere una cospicua alberatura, va navigato in tutti i suoi livelli per poter accedere ad ogni sua foglia. E se si usasse un grafo di appoggio, un po’ come avviene per JSON-LD?
Qui nasce JARQL. La libreria prende in input un JSON e lo converte in automatico in un RDF con proprietà fittizie, sfruttando i blank node e i collegamenti del grafo per emulare vettori e dizionari. Questo grafo d’appoggio può ora essere interrogato con SPARQL 1.1 costruendo a piacere ogni tripla.
Come funziona JARQL
Durante la creazione del grafo di appoggio si applicano 3 regole:
- La radice del JSON è identificata da
jarql:root
e ogni chiave si trasforma in una property dello stesso namespace. Ad esempo la chiavename
diventajarql:name
. - Un oggetto annidato diventa un blank node.
- Ogni elemento di un vettore diventa un oggetto avente la chiave del vettore stesso come predicato.
Quindi, dopo aver creato il grafo di appoggio a partire dal JSON in ingresso, JARQL applica la query SPARQL preparata per generare l’RDF desiderato.
Un esempio vale più di mille parole.
JSON di partenza
{ "parent": [{ "name": "Paperino", "children": ["Qui", "Quo", "Qua"], "fiance": {"name": "Paperina"} }, { "name": "Topolino", "children": ["Tip", "Tap", "Top"], "fiance": {"name": "Minnie"} }] }
Grafo di appoggio
@prefix jarql: <http://jarql.com/> . jarql:root jarql:parent [ jarql:name "Paperino"; jarql:children "Qui", "Quo", "Qua"; jarql:fiance [jarql:name "Paperina"] ]; jarql:parent [ jarql:name "Topolino"; jarql:children "Tip", "Tap", "Top"; jarql:fiance [jarql:name "Minnie"] ].
SPARQL
prefix : <http://example.com/> prefix jarql: <http://jarql.com/> construct { ?p :name ?n; :child [:name ?cn] . } where { jarql:root jarql:parent ?p . ?p jarql:name ?n . ?p jarql:children ?cn . }
RDF in uscita
_:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ffa <http://example.com/child> _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff9 . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ffa <http://example.com/child> _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff8 . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ffa <http://example.com/child> _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff7 . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ffa <http://example.com/name> "Paperino" . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff9 <http://example.com/name> "Qui" . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff8 <http://example.com/name> "Quo" . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff7 <http://example.com/name> "Qua" . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff6 <http://example.com/child> _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff5 . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff6 <http://example.com/child> _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff4 . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff6 <http://example.com/child> _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff3 . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff6 <http://example.com/name> "Topolino" . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff5 <http://example.com/name> "Tip" . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff4 <http://example.com/name> "Tap" . _:AX2dX60d6858cX3aX159d0c9ace6X3aXX2dX7ff3 <http://example.com/name> "Top" .
Approfondimento
Per approfondire il funzionamento di JARQL: https://jarql.github.io/
JARQL è un progetto open source: https://github.com/jarql/jarql
Synapta ospita un istanza live di JARQL, provalo subito!