Introduzione

Nello scorso numero [MOKASEMW1] abbiamo introdotto i principi base e il data model di RDF. Abbiamo inoltre fatto pratica con le API Jena e con Joseki.

Figura 1 - Sematic Web Stack: RDF & RDFS
Figura 1 - Sematic Web Stack: RDF & RDFS

In questo articolo approfondiremo RDFS e parleremo di vocabolari controllati.

Vocabolari controllati

Che cosa è un vocabolario? Un vocabolario è un elenco di parole alle quali vengono associati uno o più significati. Esempi di vocabolari sono VCARD [WPVCARD], Dublin Core [WPDC], FOAF (friend-of-a-friend) [WPFOAF] e DOAP (Description Of A Project) [WPDOAP].

Il Semantic Web estende questa nozione, con il concetto di "vocabolario controllato", ossia un set di termini non ambigui e non ridondanti sotto il controllo di un'authority che ne garantisce l'integrità e la gestione (ad esempio lo standard vCard è gestito dall'Internet Mail Consortium [IMC] mentre il Dublin Core Metadata Iniziative [DCMI] è l'organizzazione responsabile della manutenzione del Dublin Core). Ogni termine di un vocabolario RDF è associato ad un URI, che ne identifica univocamente il significato (questo risolve il problema delle parole omografe).

VCARD

VCARD (o "scheda di indirizzi virtuale") è un formato che permette lo scambio di dati personali (PDI: Personal Data Interchange) tra le applicazioni.

Gli esempi proposti lo scorso numero usavano il dizionario VCARD (definito all'URL: http://www.w3.org/2001/vcard-rdf/3.0#) e il programma Java usava le costanti definite nella classe com.hp.hpl.jena.vocabulary.VCARD.

Figura 2 - L'esempio VCARD proposto
Figura 2 - L'esempio VCARD proposto

Sempre negli esempi dello scorso articolo sono state utilizzate le costanti VCARD.FN per definire il Full Name (nome completo), VCARD.NCIKNAME per il soprannome e VCARD.EMAIl per l'email.

Ottenendo la seguente serializzazione:

<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:vcard="http://www.w3.org/2001/vcard-rdf/3.0#">
  <rdf:Description rdf:nodeID="A0">
    <vcard:EMAIL>srossini@mokabyte.it</vcard:EMAIL>
  </rdf:Description>
  <rdf:Description rdf:about="http://www.mokabyte.it/StefanoRossini">
    <vcard:N rdf:nodeID="A0"/>
    <vcard:NICKNAME>Metallian King</vcard:NICKNAME>
    <vcard:FN>Stefano Rossini</vcard:FN>
  </rdf:Description>
</rdf:RDF>

Il passo successivo prevede di mettere in relazione le due nuove risorse attraverso la nuova proprietà, ottenendo lo statement <Articolo> ha autore <Autore>.

String autore = "http://www.mokabyte.it/StefanoRossini";
// risorsa autore
String articolo = "http://www.mokabyte.it/SematicWeb";
// risorsa articolo
String mokaDicUri = "http://www.mokabyte.it/2007/04/moka_dic/1.0#";
// URI vocabolario
String hasAuthor = "hasAuthor";
// proprietà
try {
  // creazione di un Model vuoto
  Model model = ModelFactory.createDefaultModel();
  // Creo la risora articolo
  Resource articoloResource = model.createResource(articolo);
  // Creo la risora autore
  Resource autoreResource = model.createResource(autore);
  // Creo il predicato (property) => la proprietà hasAuthor
  Property hasAuthorProperty = model.createProperty(mokaDicUri, hasAuthor);
  articoloResource.addProperty(hasAuthorProperty, autoreResource);
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:j.0="http://www.mokabyte.it/2007/04/moka_dic/1.0#">
  <rdf:Description rdf:about="http://www.mokabyte.it/SematicWeb">
    <j.0:hasAuthor rdf:resource="http://www.mokabyte.it/StefanoRossini"/>
  </rdf:Description>
</rdf:RDF>
public class MOKADIC {
  protected static final String URI = "http://www.mokabyte.it/2007/04/moka_dic/1.0#";

  // Return URI
  public static String getURI() {
    return URI;
  }

  private static final String hasAuthorName = "hasAuthor";
  public static Property hasAuthor = null;

  // Istanzio le proprietà e le risorse del vocabolario
  static {
    try {
      // Istanzio la proprietà hasAuthor
      hasAuthor = new PropertyImpl(URI, hasAuthorName);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
String autore = "http://www.mokabyte.it/StefanoRossini";
// risorsa autore
String articolo = "http://www.mokabyte.it/SematicWeb";
// risorsa articolo
try {
  Model model = ModelFactory.createDefaultModel();
  // Creo la risora articolo
  Resource articoloResource = model.createResource(articolo);
  // Creo la risora autore
  Resource autoreResource = model.createResource(autore);
  // Associo alla risorsa articolo la risorsa autore
  articoloResource.addProperty(MOKADIC.hasAuthor, autoreResource);

Come si vede il nuovo codice risulta più "pulito", compatto e leggibile rispetto alla precedente versione. Con la classe wrapper la proprietà "ha autore" è definita dalla Property MOKADIC.hasAuhtor. Lo sviluppatore si deve concentrare solo nel definire il soggetto e l'oggetto per comporre correttamente lo statement: "articolo

<<ha autore>>

autore".

Come si vede il nuovo codice risulta più "pulito", compatto e leggibile rispetto alla precedente versione. Con la classe wrapper la proprietà "ha autore" è definita dalla Property MOKADIC.hasAuhtor. Lo sviluppatore si deve concentrare solo nel definire il soggetto e l'oggetto per comporre correttamente lo statement: "articolo <<ha autore>> autore".

<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:j.0="http://www.mokabyte.it/2007/04/moka_dic/1.0#">
  <rdf:Description rdf:about="http://www.mokabyte.it/SematicWeb">
    <j.0:hasAuthor rdf:resource="http://www.mokabyte.it/StefanoRossini"/>
  </rdf:Description>
</rdf:RDF>
String ns = "mokadic";
model.setNsPrefix(ns, MOKADIC.getURI());
...
articoloResource.addProperty(MOKADIC.hasAuthor, autoreResource);
...
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:mokadic="http://www.mokabyte.it/2007/04/moka_dic/1.0#">
  <rdf:Description rdf:about="http://www.mokabyte.it/SematicWeb">
    <mokadic:hasAuthor rdf:resource="http://www.mokabyte.it/StefanoRossini"/>
  </rdf:Description>
</rdf:RDF>
Map nsMap;
nsMap = new HashMap();
nsMap.put("mokadic", MOKADIC.getURI());
nsMap.put("kok", KING_OF_KINGS_CLASSIFICATION.getURI());
model.setNsPrefixes(nsMap);
private static final String tipoMokaArticolo = "MokabyteArticle";
public static Resource tipoMokaArticoloResource = null;
tipoMokaArticoloResource = new ResourceImpl(URI+tipoMokaArticolo);
addProperty(RDF.type, MOKADIC.tipoMokaArticoloResource);
<rdf:Description rdf:about="http://www.mokabyte.it/SematicWeb">
  ...
  <rdf:type rdf:resource="http://www.mokabyte.it/2007/06/moka_dic/1.0#MokabyteArticle"/>
  ...
</rdf:Description>
public class RDFS {
  protected static final String uri = "http://www.w3.org/2000/01/rdf-schema#";
  public static final Resource Class = resource("Class");
  public static final Resource Datatype = resource("Datatype");
  ...
  public static final Property comment = property("comment");
  public static final Property label = property("label");
  ...
  public static final Resource Literal = resource("Literal");
  public static final Resource Resource = resource("Resource");
}
Resource articoloResource1 = model.createResource("http://www.mokabyte.it/dd_mm_yyyy/ejb3.0")
  ...
  .addProperty(RDFS.label, "EJB 3.0")
  .addProperty(RDFS.comment, "Un articolo moka J2EE")
  ...
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
  xmlns:mokadic="http://www.mokabyte.it/2007/04/moka_dic/1.0#">
  <rdf:Description rdf:about="http://www.mokabyte.it/SematicWeb">
    ...
    <rdfs:label>EJB 3.0</rdfs:label>
    <rdfs:comment>Un articolo moka J2EE</rdfs:comment>
    ...
  </rdf:Description>
</rdf:RDF>

Attenzione: non sempre è conveniente costruire un proprio vocabolario, soprattutto quando le stesse definizioni del vocabolario che si intende costruire sono presenti in vocabolari già esistenti e controllati. A tal proposito, si vedrà ora come sviluppare lo stesso esempio utilizzando il vocabolario controllato Dublin Core. Il vantaggio del Dublin Core è quello di essere un vocabolario standard e controllato dalla Dublin Core Metadata Initiative. In Jena esiste la classe com.hp.hpl.jena.vocabulary.DC che è la classe Vocabulary wrapper che permette di utilizzare i vocaboli del Dublin Core.

Il Dublin Core

Il Dublin Core è un sistema di metadata costituito da un nucleo di elementi essenziali ai fini della descrizione di qualsiasi materiale digitale accessibile via rete informatica. Il progetto del Dublin Core si è sviluppato in ambito OCLC (Online Computer Library Center), la grande rete di servizi americana per le biblioteche. Nel marzo 1995 si è tenuta una conferenza nella città americana di Dublin (Ohio), nella quale i partecipanti (bibliotecari, archivisti, editori, ricercatori e sviluppatori di software, oltre ad alcuni membri dai gruppi di lavoro dell'IETF, Internet Engineering Task Force) si sono trovati d'accordo sulla necessità di creare un insieme di strumenti condivisi per l'accesso alle risorse digitali. Lo scopo era quello di stabilire un insieme base di elementi descrittivi che potessero essere forniti dall'autore o dall'editore dell'oggetto digitale, e in esso potessero essere inclusi o da esso potessero essere referenziati. Il consorzio di utenti che si è costituito ha incominciato così a sviluppare un'architettura per i metadata che venisse incontro alle necessità dei venditori e dei produttori di informazioni (vedere [WP_DC]).

Gli elementi costitutivi del nucleo (dicembre 1996) sono i seguenti:

  • Titolo (Title)
  • Creatore (Creator)
  • Soggetto (Subject)
  • Descrizione (Description)
  • Editore (Publisher)
  • Data (Date)
  • Tipo (Type)
  • Identificatore (Identifier)
  • Lingua (Language)
  • Relazione (Relation)
  • Riferimento a una risorsa correlata

Esempio con DC

Per l'esempio si pone l'attenzione sul vocabolo "Creatore" (DC:creator). Il "creatore" è l'entità che ha la responsabilità principale della produzione del contenuto della risorsa. Esempi di Creator includono una persona, un'organizzazione o un servizio responsabili del contenuto intellettuale della risorsa. Da un punto di vista di sviluppo, questo si traduce nell'utilizzo della classe com.hp.hpl.jena.vocabulary.DC e della proprietà DC.creator. Sostituendo nell'esempio Java la proprietà MOKADIC.hasAuhtor con la nuova DC.creator, si ha

String autore = "http://www.mokabyte.it/StefanoRossini";
// risorsa autore
String articolo = "http://www.mokabyte.it/SematicWeb";
// risorsa articolo
// create an empty model
Model model = ModelFactory.createDefaultModel();
// Create the resource and add the properties cascading style
Resource articoloResource = model.createResource(articolo)
  .addProperty(DC.creator, model.createResource(autore));
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:dc="http://purl.org/dc/elements/1.1/">
  <rdf:Description rdf:about="http://www.mokabyte.it/SematicWeb">
    <dc:creator rdf:resource="http://www.mokabyte.it/StefanoRossini"/>
  </rdf:Description>
</rdf:RDF>
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
  xmlns:foaf="http://xmlns.com/foaf/0.1/"
  xmlns:admin="http://webns.net/mvcb/">
  <foaf:PersonalProfileDocument rdf:about="">
    <foaf:maker rdf:resource="#me"/>
    <foaf:primaryTopic rdf:resource="#me"/>
    <admin:generatorAgent rdf:resource="http://www.ldodds.com/foaf/foaf-a-matic"/>
    <admin:errorReportsTo rdf:resource="mailto:leigh@ldodds.com"/>
  </foaf:PersonalProfileDocument>
  <foaf:Person rdf:ID="me">
    <foaf:name>Stefano Rossini</foaf:name>
    <foaf:title>Dott</foaf:title>
    <foaf:givenname>Stefano</foaf:givenname>
    <foaf:family_name>Rossini</foaf:family_name>
    <foaf:nick>Metallian King</foaf:nick>
    <foaf:mbox rdf:resource="mailto:srossini@mokabyte.it"/>
    <foaf:homepage rdf:resource="http:\\www.mokabyte.it\srossini"/>
    <foaf:phone rdf:resource="tel:06/0606066"/>
    <foaf:workplaceHomepage rdf:resource="www.mokabyte.it"/>
    <foaf:schoolHomepage rdf:resource="http://www.polito.it/"/>
    <foaf:knows>
      <foaf:Person>
        <foaf:name>Brother Of Metal</foaf:name>
        <foaf:mbox rdf:resource="mailto:bom@mokabyte.it"/>
      </foaf:Person>
    </foaf:knows>
    ...
    <foaf:knows>
      <foaf:Person>
        <foaf:name>Adrian</foaf:name>
        <foaf:mbox rdf:resource="mailto:adrian.sos@hell.he"/>
      </foaf:Person>
    </foaf:knows>
  </foaf:Person>
</rdf:RDF>
/** <p>The ontology model that holds the vocabulary terms</p> */
private static OntModel rdfsModel =
  ModelFactory.createOntologyModel(ProfileRegistry.RDFS_LANG);
/** <p>Categoria Java 2 Enterprise Edition </p> */
public static final OntClass J2EE = rdfsModel.createClass(URI + "J2EE");
/** <p>Categoria Java 2 Micro Edition </p> */
public static final OntClass J2ME = rdfsModel.createClass(URI + "J2ME");
...
/** <p>Categoria Integrazione</p> */
public static final OntClass J2SE = rdfsModel.createClass(URI + "J2SE");
/** <p>Categoria Metodologia</p> */
public static final OntClass J2SE = rdfsModel.createClass(URI + "J2SE");
classJava.addSubClass(classeJumpStart);
// Creo una risora articolo J2EE
Resource articoloResource1 = model.createResource("http://www.mokabyte.it/dd_mm_yyyy/ejb3.0")
  .addProperty(RDFS.label, "Esempio")
  .addProperty(RDFS.comment, "Un articolo moka J2EE")
  .addProperty(DC.publisher, "Mokabyte")
  .addProperty(DC.title, model.createLiteral("La specifica EJB 3.0", "it"))
  .addProperty(DC.creator, "Stefano Rossini")
  .addProperty(VCARD.EMAIL, "srossini@mokabyte.it")
  .addProperty(MOKADIC.hasJava, MOKADIC.J2EE)
  .addProperty(RDF.type, MOKADIC.resource);
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX mokadic: <http://www.mokabyte.it/2007/06/moka_dic/1.0#>
SELECT ?creatore ?titolo ?mail ?cat
WHERE {
  ?article dc:creator ?creatore.
  ?article dc:title ?titolo.
  ?article vcard:EMAIL ?mail.
  ?article mokadic:hasJava ?cat.
  ?article mokadic:hasJava mokadic:J2EE
}
// Creo la risora co-autore (A. Rocca)
Resource alessandroRocca = model.createResource("http://www.mokabyte.it/arocca")
  .addProperty(FOAF.name, "Alessandro")
  .addProperty(FOAF.surname, "Rocca")
  .addProperty(FOAF.mbox, "arocca@mokabyte.it")
  .addProperty(RDF.type, FOAF.Person);

// Creo la risora autore (S. Rossini) ed associo il co-autore con <foaf:knows>
Resource stefanoRossini = model.createResource("http://www.mokabyte.it/srossini")
  .addProperty(VCARD.FN, "Stefano Rossini")
  .addProperty(VCARD.EMAIL, "srossini@mokabyte.it")
  .addProperty(FOAF.knows, alessandroRocca);

// Creo la risora articolo
Resource articoloResource = model.createResource("http://www.mokabyte.it/dd_mm_yyyy/ejb3.0")
  .addProperty(RDFS.label, "Esempio")
  .addProperty(RDFS.comment, "Un articolo moka J2EE")
  .addProperty(DC.creator, stefanoRossini)
  .addProperty(DC.publisher, "Mokabyte")
  .addProperty(DC.title, model.createLiteral("MDB: Message Driven EJB", "it"))
  .addProperty(MOKADIC.hasJava, MOKADIC.J2EE)
  .addProperty(RDF.type, MOKADIC.tipoMokaArticoloResource);
<rdf:RDF
  xmlns:foaf="http://xmlns.com/foaf/0.1/"
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
  xmlns:vcard="http://www.w3.org/2001/vcard-rdf/3.0#"
  xmlns:moka="http://www.mokabyte.it/2007/06/moka_dic/1.0#"
  xmlns:dc="http://purl.org/dc/elements/1.1/">
  <rdf:Description rdf:about="http://www.mokabyte.it/arocca">
    <rdf:type rdf:resource="http://xmlns.com/foaf/0.1/Person"/>
    <foaf:mbox>arocca@mokabyte.it</foaf:mbox>
    <foaf:surname>Rocca</foaf:surname>
    <foaf:name>Alessandro</foaf:name>
  </rdf:Description>
  <rdf:Description rdf:about="http://www.mokabyte.it/dd_mm_yyyy/ejb3.0">
    <rdf:type rdf:resource="http://www.mokabyte.it/2007/06/moka_dic/1.0#MokabyteArticle"/>
    <moka:hasJava rdf:resource="http://www.mokabyte.it/2007/06/moka_dic/1.0#J2EE"/>
    <dc:title xml:lang="it">MDB: Message Driven EJB</dc:title>
    <dc:publisher>Mokabyte</dc:publisher>
    <dc:creator rdf:resource="http://www.mokabyte.it/srossini"/>
    <rdfs:comment>Un articolo moka J2EE</rdfs:comment>
    <rdfs:label>Esempio</rdfs:label>
  </rdf:Description>
  <rdf:Description rdf:about="http://www.mokabyte.it/srossini">
    <foaf:knows rdf:resource="http://www.mokabyte.it/arocca"/>
    <vcard:EMAIL>srossini@mokabyte.it</vcard:EMAIL>
    <vcard:FN>Stefano Rossini</vcard:FN>
  </rdf:Description>
</rdf:RDF>