Schlagwort-Archive: RDF

Bestellübersicht im Solid Pod: Unstrukturierte Infos mithilfe von ChatGPT konvertieren

Wie behaltet ihr den Überblick über eure Online-Bestellungen? Es kann schwierig sein, sich an alle Details zu erinnern, insbesondere wenn man in vielen verschiedenen Shops einkauft. Solid kann euch helfen, alle Bestellungen zentral in eurem Pod zu speichern und zu organisieren.

Aber habt ihr jemals versucht, unstrukturierte Daten wie E-Mail-Bestellbestätigungen im Pod zu speichern? Wenn ja, wisst ihr sicherlich, wie mühsam und fehleranfällig das sein kann, da viele Shops nur HTML- oder reine Text-Bestätigungen liefern. Das Erstellen von RDF von Hand ist aufwändig und fehleranfällig und daher keine praktikable Lösung. In diesem Blogpost möchte ich euch zeigen, wie ihr mit ChatGPT unstrukturierte Daten aus E-Mail-Bestellbestätigungen und anderen Textquellen im Solid Pod speichern könnt.

ChatGPT, ein von OpenAI trainiertes Sprachmodell, kann unstrukturierten Text in strukturierte Daten umwandeln und damit auch das Erstellen von RDF ermöglichen. Dies ist in allen Fällen hilfreich, in denen man keine strukturierten Daten hat, aber ein interoperables Format für Solid benötigt.

Ich habe genau das nun ausprobiert, um eine E-Mail-Bestellbestätigung in RDF umzuwandeln, und war beeindruckt! Zwar musste ich noch ein paar Fehler manuell korrigieren, aber im Vergleich zur manuellen Übertragung aller Daten war das immer noch eine große Zeitersparnis.

Hier ist ein Beispielprompt, den ich ChatGPT gegeben habe:

Konvertiere folgende Bestelldetails nach RDF:

Ihre Bestellung Nummer 3456789 vom 08.12.2022

Carenesse Aleppo Soap, 60% Olive Oil 40% Laurel Oil, Approx. 200 g, original olive oil soap, natural product, vegan handmade shower soap, natural soap
Verkauf durch: Carenesse - natürlich schön
Rücksendung bis zum 31.01.2023 möglich.
EUR 11,69
Zustand: Neu - Carenesse Qualität, garantiert echt nur mit Carenesse-Etikett/Label, nur bei Carenesse

Und hier ist das beeindruckende Ergebnis, das ich von ChatGPT schlussendlich erhalten habe:

@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix xsd:    <http://www.w3.org/2001/XMLSchema#> .
@prefix schema: <http://schema.org/> .

<#this>
    a schema:Order ;
    schema:orderNumber "3456789" ;
    schema:orderDate "2022-12-08T00:00:00Z"^^xsd:dateTime ;
    schema:acceptedOffer [
        a schema:Offer ;
        schema:itemOffered [
            a schema:Product ;
            schema:name "Carenesse Aleppo Soap" ;
            schema:description "60% Olive Oil 40% Laurel Oil, Approx. 200 g, original olive oil soap, natural product, vegan handmade shower soap, natural soap" ;
            schema:seller [
                a schema:Organization ;
                schema:name "Carenesse - natürlich schön"
            ] ;
        ] ;
        schema:price [
            a schema:PriceSpecification ;
            schema:price "11.69" ;
            schema:priceCurrency "EUR"
        ] ;
        schema:itemCondition "http://schema.org/NewCondition" ;
        schema:description "Rücksendung bis zum 31.01.2023 möglich."
    ] .

Man beachte, dass ChatGPT hier verschiedene Elemente der Bestellung wie Bestellnummer, Artikel, Preis und Verkäufer erkannt und strukturiert hat. Es war faszinierend, fast schon magisch, zu sehen, wie ChatGPT aus meinen unstrukturierten Bestelldetails RDF erzeugt hat.

Wenn ihr also auch Schwierigkeiten habt, strukturierte Daten aus z.B. E-Mail-Bestellbestätigungen zu gewinnen und in eurem Solid-Pod zu speichern, kann ChatGPT eine interessante Option sein. Allerdings sollte man dabei beachten: ChatGPT ist ein Cloud-Dienst, was bedeutet, dass ihr eure Daten an einen externen Anbieter übertragen müsst. Dies passt nicht ganz zum Ziel von Solid, volle Kontrolle über die eigenen Daten zu behalten. Mit Bedacht eingesetzt kann es aber sehr hilfreich sein und hat enormes Potential.

WordPress Linked Data Plugin installieren

Mein WordPress-Plugin zur Veröffentlichung von Bloginhalten als Linked Data, kurz wp-linked-data, ist nun auch über das WordPress-Plugin-Repository verfügbar. Die Installation ist mit wenigen Klicks erledigt. Wie die meisten anderen Plugins könnt ihr es nun über eueren WordPress-Administrationsbereich finden und installieren. Sucht einfach nach „wp-linked-data“ oder „Linked Data“.

Auch eine offizielle Seite bei wordpress.org hat es nun. Ich freue mich über Bewertungen und Feedback.

WordPress Bloginhalte als Linked Data

Dieses Blog veröffentlicht nun selbst Linked Data, statt nur darüber zu berichten. Dazu habe ich ein kleines WordPress-Plugin geschrieben, das die Bloginhalte über Content-Negotiation auch in verschiedenen RDF-Formaten verfügbar macht.

Blogartikel als „abstraktes Ding“, nicht als HTML-Dokument, bekommen eine eigene, um #it erweiterte URI. Das Web-Dokument, das gerade in eurem Browser geöffnet ist, ist z.B. unter http://datenwissen.de/2013/04/wordpress-bloginhalte-als-linked-data erreichbar. Der Blogartikel den ihr lest, hat also den Identifier http://datenwissen.de/2013/04/wordpress-bloginhalte-als-linked-data#it

Ihr könnt die URI, z.B. mit einem Online-RDF-Browser abrufen, um die Daten zu sehen. Freunde der Konsole nutzen curl und setzten einen entsprechenden Accept-Header (text/turtle oder application/rdf+xml):

curl -H "Accept: text/turtle" http://datenwissen.de/2013/04/wordpress-bloginhalte-als-linked-data#it

Auch Blog-Autoren bekommen vom Plugin eine eigene URI, die in späteren Versionen als WebID nutzbar sein wird. Die URI der Autorenseite wird um #me erweitert, um die Person zu identifizieren. In diesem Blog werde ich also durch http://datenwissen.de/author/angelo#me identifiziert. Ruft die URI wie oben beschrieben ab, um das FOAF-Profil zu sehen, das sich dahinter verbirgt.

Update: Die Installation ist nun einfach über die WordPress-Admin-Oberfläche durchführbar. Den Quellcode gibt es natürlich trotzdem noch auf GitHub.
Das Plugin befindet sich noch nicht im WordPress-Plugin-Repository, ist jedoch bereits auf GitHub verfügbar. Kopiert einfach den Inhalt des /src-Verzeichnisses unter /wp-content/plugins/wp-linked-data und aktiviert das Plugin in der WordPress-Oberfläche.

groovyrdf vereinfacht WebID-Publizierung

In Version 0.2.1 vereinfacht groovyrdf nun auch die Veröffentlichung von RSA-Public-Keys. Dazu muss einfach die Methode ‚publicKey‘ innerhalb der den Key besitzenden Resource aufgerufen werden. In folgendem Beispiel bekommt Alice einen Public Key zugewiesen:

RdfData rdfData = rdfBuilder {
  "http://example.com/resource/alice" {
    publicKey (
      '#alicePublicKey',
      label: 'Public Key of Alice',
      modulus: '2cbf8fff963dea33ee7d4f007ae',
      exponent: 65537
    )
  }
}

Der obige Code erzeugt RDF gemäß der aktuellen WebID-Spezifikation:


<http://example.com/resource/alice>
 <http://www.w3.org/ns/auth/cert#key>
 <#alicePublicKey>.

<#alicePublicKey>
 a <http://www.w3.org/ns/auth/cert#RSAPublicKey>;
 <http://www.w3.org/2000/01/rdf-schema#label>
   "Public Key of Alice";
 <http://www.w3.org/ns/auth/cert#exponent>
   65537;
 <http://www.w3.org/ns/auth/cert#modulus>
   "2cbf8fff963dea33ee7d4f007ae"^^<http://www.w3.org/2001/XMLSchema#hexBinary> .

groovyrdf steht hier zum Download bereit. Ein ausführlicher User-Guide ist hier verfügbar.

groovyrdf 0.2 veröffentlicht

Es ist mittlerweile schon über 1 Jahr her, dass ich mit groovyrdf eine Bibliothek zur einfachen Erzeugung von RDF-Daten mit Groovy veröffentlicht habe. Nun ist endlich das 2. Release da, und es bringt interessante Neuerungen mit sich.

Neben dem Erzeugen von RDF ist mit Version 0.2 auch das elegante Auslesen und Verarbeiten von Daten möglich. Mittels RdfLoader unterstützt groovyrdf das Laden von Linked Data Ressourcen inklusive Content-Negotiation. Die Daten können anschließend ausgelesen und weiter verarbeitet werden:

  // Namespace-Deklaration
  def foaf = new RdfNamespace ('http://xmlns.com/foaf/0.1/')

  // Laden einer Ressource mittels RdfLoader
  RdfLoader rdfLoader = new JenaRdfLoader()
  RdfResource person = rdfLoader.loadResource(
    'http://me.desone.org/person/aveltens#me'
  )

  println person(foaf.name) // Gibt 'Angelo Veltens' aus

Der Loader kümmert sich selbstständig um Content-Negotation und das Parsen des zurückgelieferten Formats. Solange RDF geliefert wird, kann es uns egal sein, ob dies vom Server in TURTLE, RDF/XML oder N3 ausgedrückt wird. Alle gängigen RDF-Syntaxen werden unterstützt. Der Zugriff auf die Eigenschaften der geladenen Resouce ist anschließend einfach über resource(predicateUri) möglich. Näheres erklärt der User Guide. Version 0.2 steht hier zum Download bereit und den Sourcecode gibt es nach wie vor bei GitHub.

[important]Wenn ihr groovyrdf nutzt, oder auch einfach nur kurz ausprobiert, freue ich mich über euer Feedback![/important]

Linked Data: Mehr als nur RDF

Das Beispiel offenerhaushalt.de zeigt, dass zur Partizipation am Web of Data mehr gehört, als die Veröffentlichung von RDF/XML-Daten. Es lohnt sich dennoch, den Schritt zu „Linked Data“ zu gehen.

Die Veröffentlichung von Daten im RDF-Format gehört zu den Linked Data Grundprinzipien, die Tim Berners-Lee aufgestellt hat. Daher freue ich mich, dass immer mehr Open-Data-Projekte neben JSON und XML, auch Daten in einer RDF-Syntax veröffentlichen.

Das allein reicht aber leider nicht aus, um von Linked Data sprechen zu können, wie ich am Beispiel der RDF/XML-Daten von offenerhaushalt.de zeigen möchte.

Rufen wir uns zunächst die Grundprinzipien noch einmal ins Gedächtnis:

  1. Use URIs as names for things
  2. Use HTTP URIs so that people can look up those names
  3. When someone looks up a URI, provide useful information, using the standards (RDF, SPARQL)
  4. Include links to other URIs. so that they can discover more things

Ich werde diese Prinzipien nun nacheinander mit den RDF-Daten des Haushaltspostens „Bundesministerium für Bildung und Forschung“ von offenerhaushalt.de abgleichen, welche sich im Dokument http://bund.offenerhaushalt.de/30.rdf befinden.

[important]Disclaimer: Auch wenn ich in Sachen Linked Data in diesem Artikel einiges an offenerhaushalt.de zu kritisieren habe, möchte ich dies nicht als generelle Kritik am Projekt verstehen. Im Gegenteil: Ich halte das Projekt für ein Vorzeigebeispiel der Open-Data-Bewegung und spreche allen Beteiligten großen Dank und Respekt aus! Meine Kritik ist im Vergleich zu dem, was bereits geleistet wurde, eine Kleinigkeit. Ich möchte sie eher als Anregung verstanden wissen. Auch bin ich gerne bereit zu helfen, wenn Interesse besteht, die offenen Haushaltsdaten ins Web of Data zu hieven.[/important]

Use URIs as names for things

Wesensmerkmal des Web of Data ist, dass dort nicht nur reine Dokumente („Informationsressourcen„), sondern auch Daten über reale und abstrakte „Dinge“ aufzufinden sind. Diese Dinge benötigen einen global eindeutigen Namen. Genau diese Anforderung erfüllen URIs.

Bei der Namensgebung ist zu beachten, dass das „Ding“ welches beschrieben wird und die Dokumente welche es beschreiben unterschiedlich sind und deshalb auch über unterschiedliche URIs identifiziert werden müssen. Getreu dem Merksatz: „Du bist nicht deine Website„!

Das Datendokument http://bund.offenerhaushalt.de/30.rdf beschreibt ein Subjekt namens http://bund.offenerhaushalt.de/30. Unklar bleibt dabei, um was für eine Art von „Ding“ es sich dabei handelt. Ein rdf:type ist nicht hinterlegt.

Nun liegt die Vermutung nahe, dass bei offenerhaushalt.de Haushaltsposten beschrieben werden. Ruft man jedoch die URI http://bund.offenerhaushalt.de/30 im Browser oder mit Hilfe eines Tools wie curl ab, so wird ein HTML-Dokument zurückgeliefert:

user@pc:~$ curl -I http://bund.offenerhaushalt.de/30
HTTP/1.1 200 OK
Date: Wed, 02 May 2012 18:38:32 GMT
Server: Apache/2.2.14 (Ubuntu)
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 73434
Vary: Accept-Encoding
Content-Type: text/html; charset=utf-8

Auch wenn man den Accept-Header explizit auf application/rdf+xml oder text/turtle setzt, wird ausschließlich text/html zurück geliefert.

Bei http://bund.offenerhaushalt.de/30 handelt es sich also schlichtweg um ein herkömmliches HTML-Dokument und nicht um einen Haushaltsposten.

Interessant sind unter diesem Aspekt Aussagen wie zum Beispiel folgende¹:

<http://bund.offenerhaushalt.de/30> openhaushalt:is_department "true".

Das HTML-Dokument ist also eine Abteilung, soso 🙂

Wobei wir streng genommen gar nicht wissen können, was openhaushalt:is_department überhaupt bedeutet. Das Prädikat ist nämlich nicht auflösbar, womit wir beim nächsten Punkt angelangt sind:

Use HTTP URIs so that people can look up those names

Die URI des Prädikats erhält man, indem man das Namespace-Prefix durch die Namespace-URI ersetzt. Für openhaushalt:is_department ergibt sich somit http://offenerhaushalt.de/schema/0.3/is_department. Bei Abruf dieser URI erhält man leider nichts weiter als den HTTP-Fehlercode 404: Not Found. Zwar wurden HTTP-URIs verwendet, diese sind aber nicht abrufbar.

Provide useful information

Bei den abrufbaren URIs scheitert es an „nützlichen Informationen“. Wie schon im ersten Abschnitt bemerkt, liefert http://bund.offenerhaushalt.de/30 lediglich ein HTML-Dokument zurück, wohingegen die RDF-Daten eher auf ein abstraktes „Ding“, wie einen Haushaltsposten oder eine Abteilung, hindeuten.

Dieses „Ding“ müsste zunächst einmal einen eigenen Namen bekommen, z.B. http://bund.offenerhaushalt.de/30#item

Beim Abruf mittels Browser würde durch den Wegfall des Fragment-Identifiers weiterhin das HTML-Dokument http://bund.offenerhaushalt.de/30 ausgeliefert werden, wodurch die „nützlichen Informationen“ zumindest für den menschlichen Klienten weiterhin gewährleistet wären.

Fordert ein Client mittels HTTP-Accept-Header jedoch ausdrücklich RDF, dann muss dieses ebenfalls über die URI http://bund.offenerhaushalt.de/30#item respektive http://bund.offenerhaushalt.de/30 verfügbar sein, da der Client entscheidet, welches Datenformat für ihn nützlich ist. Derzeit wird aber immer HTML ausgeliefert.

Nützlich sind die Daten aber auch deshalb nicht, weil die URIs der Prädikate nicht abrufbar sind. Die verwendete Ontologie ist somit ohne API-Dokumentation nicht zu verstehen. Es bleibt unklar, was Prädikate wie openhaushalt:is_department tatsächlich bedeuten. Die RDF-Daten sind damit kein Zugewinn im Vergleich zu z.B. JSON. Die Semantik fehlt in beiden Varianten.

Include links to other URIs

Die Verlinkung beschränkt sich auf andere URIs innerhalb des Projekts offenerhaushalt.de. Eine Verlinkung ins offene Web of Data findet derzeit nicht statt. Problematisch ist auch hier wieder, dass die URIs lediglich HTML-Dokumente identifizieren und somit auch kein „internes Web of Data“ entsteht. Was bleibt sind RDF/XML-Dokumente mit Links zu HTML-Seiten.

Na und?

Nun stellt sich dem ein oder anderen sicher die Frage, wozu man diese manchmal recht akademisch anmutenden Ansätze überhaupt verfolgen sollte? Die offenen Haushaltsdaten, die derzeit vom Projekt angeboten werden, sind zweifelsohne bereits in der vorliegenden Form sehr wertvoll. Die vorliegenden RDF/XML-Daten bieten aber nach meiner Auffassung keinen Mehrwert gegenüber dem JSON-Format. Im Gegenteil: Sie sind für einen Programmierer eher noch schwerer zu verarbeiten.

In Form von Linked Data können die Daten aber ein Potential entfalten, das deutlich über den Status quo hinaus geht. Eindeutige URIs für Haushaltsposten (nicht die HTML-Seiten!) könnten z.B. auch von anderen Projekten genutzt werden um strukturierte Aussagen über diese Haushaltsposten zu treffen. Denkbar wären zum Beispiel Tools zum Bürgerhaushalt, welche ihre Nutzer die Budgets für die einzelnen Haushaltsposten neu verteilen lassen. Dadurch dass alle über die gleichen (durch URIs global benannten!) Haushaltsposten „sprechen“, ist es dabei völlig egal, auf welchem Server mit welchem Tool solch ein Bürgerhaushalt erstellt wurde. Man kann sich das ähnlich vorstellen wie heute Verlinkungen und Pingbacks in der Blogosphäre, nur dass eben nicht über Dokumente/Blogeinträge „gesprochen“ wird, sondern über Daten und Dinge, wie z.B. Haushaltsposten.

Die Verwendung von einheitlichen Ontologien macht diesen Austausch ebenfalls leichter. Zudem wird eine „API“-Dokumentation letztlich überflüssig, wenn die Prädikate der Ontologien im Web abrufbar sind uns sich auf diese Weise selbst erklären.

Fazit

Das Projekt offenerhaushalt.de stellt RDF/XML-Daten bereit, ist damit aber noch kein Teil des Web of Data. Die folgenden Kritikpunkte zeigten sich bei einem Abgleich mit den Linked Data Prinzipien von Tim Berners-Lee:

  1. URIs identifizieren lediglich Dokumente, aber keine „Dinge“ wie Haushaltsposten.
  2. Die „Ontologie“ besteht nur aus toten Links und ist damit genauso ausdruckslos wie JSON.
  3. Es findet keine Content-Negotiation statt, die Wünsche des Clients werden ignoriert.
  4. Aufgrund von 1. kann es auch keine Links auf „Dinge“ geben. Es entsteht somit kein Web of Data.
  5. Es gibt keine Verlinkungen ins Web of Data.

Werden diese Aspekte angepasst und die Linked Data Prinzipien beachtet, können die Daten von offenerhaushalt.de Teil des Web of Data werden und dadurch ihr volles Potential entfalten.


¹ Zur besseren Lesbarkeit verwende ich hier die TURTLE-Notation, obwohl die Daten im Original als RDF/XML vorliegen. Semantisch macht dies keinen Unterschied. 

Linked Data Patterns

Wie wähle ich meine URIs richtig? Wie strukturiere ich meine RDF-Daten? Und wie verarbeite ich im Web verfügbare Daten am besten?

Auch im Themenkomplex Linked Data gibt es immer wiederkehrende Problemstellungen, die von anderen längst gelöst wurden. Zum Glück stehen uns auch hier eine Reihe von Patterns und Best Practices zur Verfügung. Leigh Dodds und Ian Davis haben diese in „Linked Data Patterns“ gesammelt und beschrieben. Sicher nichts was man an einem Stück durchliest, aber ein gutes Nachschlagewerk.

Das Buch ist online verfügbar und steht unter einer Creative Commons Lizenz. Es lässt sich als PDF und im EPUB-Format herunterladen.

Linked Data Social Network

Ich beschäftige mich nicht nur theoretisch mit den Themen Linked Data und dezentralen sozialen Netzwerken, sondern arbeite derzeit auch ganz konkret an einer Social Network Plattform auf Basis von Linked Data. Dass was ich seit gestern online habe, würde ich aber nicht mal als Pre-Alpha bezeichnen, also erwartet nicht zu viel, falls ihr auf den gleich folgenden Link klickt.

Mein Projekt „Desone“ (Decentralized Social Network) hat derzeit auch noch wenig mit „Networking“ zu tun. Es ist eher ein Profil-Hoster und derzeit auch nur für mein eigenes Profil (Anmeldung ist noch nicht möglich, klickt so oft auf Login wie ihr wollt). Editierfunktionen bietet es auch noch nicht (nichtmal für mich selbst). Kommt aber alles noch!

Zum „Network“ wird das ganze aber dann doch wieder, weil Desone natürlich Linked Data bereitstellt. In meiner Bekanntenliste verlinke ich auf meine Kontakte bei identi.ca (StatusNet stellt praktischerweise FOAF bereit) und Twitter (semantictweet.com machts möglich) sowie auf einige wenige Personen die selbst ein FOAF-Profil hosten.

Meine Interessen verlinken zudem auf die DBpedia und mein Wohnort auf geonames.org. Wie ihr seht, sind dank Linked Data also schon sehr viele Angaben möglich, ohne das Desone selbst diese Informationen (z.B. Orte, Interessen) hosten muss. Desone hostet nur das Profil, den Rest stellt praktischerweise das Web of Data bereit.

Genug der langen Rede, schauts euch an, wenn ihr mögt: http://me.desone.org/person/aveltens#me

An die RDF-Daten kommt ihr dank Grails Content-Negotiation auf folgenden Wegen:

Ich werde euch natürlich über das Projekt auf dem Laufenden halten.

Content-Negotiation mit Grails

Content-Negotiation, zu Deutsch Inhaltsvereinbarung, ermöglicht es dem Webserver unterschiedliche Repräsentationen einer Resource an den Client auszuliefern und ist damit eine wichtige Funktion im Web of Data. Nehmen wir zum Beispiel an, die folgende URI identifiziert die Stadt Berlin:

http://example.com/city/berlin

Web-Browser fordern für gewöhnlich HTML-Dokumente an und sollen daher beim Abruf dieser URI eine schön aufbereitete Webseite mit Informationen über Berlin und ggf. ein paar Bilder ausgeliefert bekommen. Gleichzeitig sollen jedoch auch RDF-Daten über Berlin im Web of Data verfügbar gemacht werden.

In bin ein großer Freund des Grails Web-Frameworks, welches ich sowohl beruflich als auch privat nutze. Mit Grails wird – neben so ziemlich allem was Web-Entwicklung angeht – auch die Content-Negotiation zum Kinderspiel. Ich möchte das an dem bereits angesprochenen Beispiel praktisch verdeutlichen. (Ich gebe allerdings keine Einführung in Grails selbst, dazu verweise ich auf die hervorragende Dokumentation)

Nehmen wir an es gibt eine Domainklasse City, welche in verschiedenen Repräsentationen durch den CityController ausgeliefert werden soll.

Wir legen folgendes Mapping in UrlMappings.groovy an:

static mappings = {
  "/city/$name"(resource: "city")
}

Dadurch werden die Requests an URLs der Form /city/name RESTful gemappt und an den CityController weitergeleitet. D.h. GET-Requests gehen automatisch an die show-Action des Controllers POST an save u.s.w. (Details siehe Grails-Dokumentation). Außerdem wird der Part hinter /city/ als Parameter „name“ in die Action hineingegeben.

Mithilfe des withFormat-Ausdrucks kann der Controller auf verschiedene Anfrageformate unterschiedlich reagieren:

class CityController {
  def rdfRenderer

  def show = {
    def city = City.findByName(params.name)
    withFormat {
      html { return [cityInstance: city] }
      json { render city as JSON }
      rdf { rdfRenderer.render(city) }
    }
  }
}

Der rdfRenderer ist ein fiktives Beispiel. Sowas wird leider nicht von Grails mitgeliefert. RDF lässt sich aber sehr leicht mit groovyrdf generieren. Die Codeblöcke werden bei Anfragen nach den entsprechenden Formaten ausgeführt. html und json liefert Grails von Haus aus mit, was rdf bedeutet muss man jedoch kurz selbst in der Config.groovy bekannt geben:

grails.mime.types = [
  html: ['text/html','application/xhtml+xml'], // bereits vorkonfiguriert
  json: ['application/json','text/json'], // bereits vorkonfiguriert
  // [...]
  rdf: 'application/rdf+xml' // selbst ergänzt
]

Das wars im Prinzip schon, leider gibts in Grails derzeit noch einen kleinen Bug: Grails liest für die Content-Negotiation nicht wie üblich den HTTP-Accept-Header aus, sondern den Content-Type. Der Content-Type gibt eigentlich das Format der Daten an, welche man hinsendet und nicht der Daten welche man anfordert. Das lässt sich aber leicht über eine einfache Konfiguration beheben. Dazu in der Config.groovy folgende Einstellung setzen:

grails.mime.use.accept.header = true

Das wars auch schon! Abhängig vom gesetzten Accept-Header reagiert unser Controller nun anders auf den Request und kann entsprechend eine GSP rendern um eine HTML-Seite auszuliefern, JSON-Daten ausgeben oder aber RDF! Und sogar folgende URIs sind gültig:

http://example.com/city/berlin.html - Liefert immer die HTML-Seite
http://example.com/city/berlin.json - Liefert immer JSON-Daten
http://example.com/city/berlin.rdf - Liefert immer RDF-Daten

Wir haben also mit http://example.com/city/berlin eine eindeutige URI, welche die Resource Berlin identifiziert und zum anfragenden Client passende Daten ausliefert. Zudem haben wir für jede der Repräsentationen eine URI mit der entsprechenden „Dateiendung“. Das ist genau das, was wir im Web of Data brauchen 🙂

Das Web als Social Network

Linked Data eignet sich hervorragend um Soziale Netzwerke im Web abzubilden, die genauso dezentral funktionieren wir das altbekannte WWW. Wie ich bereits erklärt habe, gibt es im „Web of Data“ nicht nur Dokumente, sondern alle möglichen „Dinge“ können durch URIs identifiziert werden. Zu diesen „Dingen“ gehören auch Personen und ihre Beziehungen.

Zunächst müssen wir uns dazu noch einmal ins Gedächtnis rufen, dass Personen und die Dokumente, die sie beschreiben, zwei völlig unterschiedliche Dinge sind: You are not your Website!

Folgende URI identifiziert mich als Person: http://data.kontroversen.de/foaf.rdf#me

Man beachte das #me am Ende! Diese URI ist eine andere URI als http://data.kontroversen.de/foaf.rdf (ohne #me). Beide URIs führen im Web-Browser zwar zum gleichen RDF-Dokument, nur die letztere jedoch identifizert das RDF-Dokument, während http://data.kontroversen.de/foaf.rdf#me die Person „Angelo Veltens“ identifiziert.

Das abgerufene Dokument enthält RDF-Daten über die Resource http://data.kontroversen.de/foaf.rdf#me (mich), unter anderem meinen Namen und ein Verweis auf ein Bild von mir. Die Datei ist damit so etwas ähnliches wie ein Social-Network-Profil und legt damit den Grundstein für ein dezentrales soziales Netzwerk mit Hilfe von Linked Data.

Aber wie zur Hölle kann ich jetzt Freundschaften schließen, Status-Updates senden, Bilder sharen, Sachen liken, kommentieren, anstupsen, retweeten?! Eins nach dem anderen.

Beziehungen zwischen Personen sind sehr leicht über RDF-Tripel zum Ausdruck zu bringen. Die FOAF-Ontologie bietet zum Beispiel das Prädikat foaf:knows um zu sagen „A kennt B“. (Da FOAF sich prima für Social Networks eignet, ist auch häufig vom „FOAF-Profil“ die Rede, auch wenn natürlich weitere Ontologien im Profil verwendet werden können) Ein Beispiel aus meinem Profil:

<http://data.kontroversen.de/foaf.rdf#me>
  <foaf:knows>
    <http://fcns.eu/people/andrei/card#me>

Hinter http://fcns.eu/people/andrei/card#me verbirgt sich Andrei Sambra, was ein einfacher Abruf der URI verrät. Dank Content-Negotiation liefert ein normaler Webbrowser bei seiner URI sogar eine menschenlesbare HTML-Datei aus. Das habe ich für mein Profil leider noch nicht eingerichtet, kommt aber noch!

Jetzt mag das alles für Laien noch etwas kompliziert erscheinen, aber genausowenig wie man heute nicht mehr manuell an HTML-Dateien herumeditieren muss um ein Blog zu betreiben, wird man schon bald kein RDF mehr verstehen müssen um sein eigenes Profil im Social Web zu hosten. Es wird Anbieter geben, bei denen man mit wenigen Klicks ein Profil anlegen kann und wer mag installiert sich eine kleine Software auf seinem eigenen Webspace / Server um sein Profil zu hosten. Der Knackpunkt ist: Egal wo auch immer euer Profil liegt und wo die eurer Freunde liegen: Ihr könnt euch miteinander vernetzen. Ihr könnt euch nennen wie ihr möchtet. Ihr allein könnt bestimmen wem ihr welche Daten preisgebt. Ihr seid nicht von der Willkür einzelner Anbieter wie Google und Facebook abhängig. Das Web wird zum Social Network. Das gesamte Web mit all seinen Inhalten und Möglichkeiten!

Damit sind wir beim nächsten Punkt: Alles im Web lässt sich über Linked Data verknüpfen. Fotos, Videos, Blogartikel… Alles was im Web ist, hat auch eine URI und ich kann damit Aussagen treffen wie z.B.

<http://data.kontroversen.de/foaf.rdf#me>
  <ex:likes>
    <http://datenwissen.de/2011/09/das-web-als-social-network/>

um kenntlich zu machen, dass ich diesen Blogartikel „mag“. Ich kann Bilder hochladen wo immer ich möchte und sie mit meinem Profil verknüpfen. Ich kann Sie auf meinen eigenen Server laden. Oder bei Flickr. Das Social Web ist vollkommen dezentral. Nicht nur sind die einzelnen Profile auf viele verschiedenen Server verteilt, auch die Daten meines Profils kann ich beliebig im Web verteilen. Ein paar Bilder auf Flickr, ein paar in meinem Blog, Status-Updates bei Anbieter A, Social Bookmarks bei Anbieter B. Und das alles mit nur einem einzigen Profil, dass sich unter meiner Kontrolle befindet.

Um dies technisch zu ermöglichen muss ich mich in irgendeiner Form auf anderen Servern, bzw. bei den unterschiedlichen Anbietern mit meinem Profil „einloggen“ können. Erfreulicherweise arbeitet das W3C gerade an WebID, einem Protokoll, welches bereits heute mit allen Browsern funktioniert, da es auf SSL-Zertifikaten basiert, welche schon seit Jahrzehnten unterstützt werden. Mit WebID ist ein Login per Mausklick, ohne Username und Passwort, möglich. Ich werde das aber in einem späteren Artikel erklären, für heute soll es erst einmal reichen.

Ich hoffe es ist ungefähr klar geworden, wie ein dezentrales Social Web mit Hilfe von Linked Data möglich wird. Ansonsten zögert nicht Rückfragen zu stellen! Wer gleich ausprobieren möchte, was heute bereits möglich ist, der kann sich bei http://foaf.me/ oder http://webid.fcns.eu/ ein FOAF-Profil anlegen und sich mit mir verlinken. Egal wo ihr eurer Profil eingerichtet habt, könnt ihr dann hier etwas an die Wall posten.