blog archive contact about feed

Rob's Blog - October 2004

Contents

Here are Rob's Blog entries for October 2004.

Blog entries for other months can be found in the main blog index.

Clocks Going Back For Winter

I was sitting downstairs at my parents house, just having got back from Maxims in Eastbourne, when I heard I whirring sound.

It turned out the sound came from the radio controlled clock that takes updates from the time signal in Rugby.

I snapped the clock with my Nokia 7610 phone, and here is a quick video of the clock adjusting itself.

The clock turns itself forward by 11 hours to go back.

Entered: 2004-10-31 01:35:09
Modified: 2004-10-31 01:37:45
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=435

Querying RDF In Perl with RDF::Redland

Previously I've shown how to parse and query RDF files using the Perl RDF::Core modules.

If you are serious about using RDF with Perl then you should take a look at Dave Beckett's excellent Redland framework.

Redland is an free, open source C library for parsing, storing and querying RDF files. The Perl bindings can be found on CPAN as RDF::Redland.

Redland has several advantages over some of the other Perl RDF libraries. It's small, cross platform, very fast, standards compliant and written by one of the authors of the RDF specification for the W3C. The downside is that it can be a bit tricky to install and some of the Perl documentation is lacking in depth.

Lets have a look at parsing and querying my FOAF file to extract the same information as in my previous article How To Query RDF In Perl Using RDF::Core.

Firstly we need to setup Redland. It works in a similar way to RDF::Core in that it you have to define some storage and create a model to use. In this example we'll use memory, but Redland also lets you bind with various databases. See the documentation in RDF::Redland::Storage and Redland itself for more details.

my $storage = new RDF::Redland::Storage("memory"); die "unable to create storage" unless $storage; my $model = new RDF::Redland::Model($storage, ""); die "unable to create model" unless $model;

Next we need to define the file we want to parse. In this case it's a local file called foaf.rdf. Redland uses an RDF::Redland::URI object to define each data source.

my $uri = new RDF::Redland::URI("file:foaf.rdf");

This RDF file is in XML format, so we'll need to create a parser capable to reading it.

my $parser = new RDF::Redland::Parser("rdfxml", "application/rdf+xml"); die "unable to create parser" unless $parser;

We have all we need to parse the file, so lets do it. We'll need to add each triple the parser finds to the $model so we query it later.

my $stream = $parser->parse_as_stream($uri, $uri); while(!$stream->end) { $model->add_statement($stream->current); $stream->next; }

At this stage we have all the triples in our model ready to query. We'll use Redland's query functionality (provided by the rasqal library) to extract information about Cal from my FOAF file. The triples we want are in this bit of RDF.

<foaf:knows> <foaf:Person> <foaf:nick>Cal</foaf:nick> <foaf:name>Cal Henderson</foaf:name> <foaf:mbox_sha1sum>2971b1c2fd1d4f0e8f99c167cd85d522a614b07b</foaf:mbox_sha1sum> <rdfs:seeAlso rdf:resource="http://www.iamcal.com/foaf.xml"/> </foaf:Person> </foaf:knows>

For a detailed explanation of the above, see my article What Is An RDF Triple.

Redland uses RDQL to query the data. This is a very powerful query language that looks a bit SQL and RDF::Core's query language. We have to provide a match for each triple we are looking for.

Let's get an RDQL query to get Cal's name, nickname, FOAF file URL and mbox checksum into a variable called $string.

my $string = <<EOF_QUERY; SELECT ?name ?nick ?seeAlso ?mbox_sha1sum WHERE (?x rdf:type foaf:Person), (?x foaf:name ?name), (?x rdfs:seeAlso ?seeAlso), (?x foaf:mbox_sha1sum ?mbox_sha1sum), (?x foaf:nick ?nick) AND (?nick eq 'Cal') USING foaf FOR <http://xmlns.com/foaf/0.1/> EOF_QUERY

In the SELECT saying we want to extract the data represented by the place holders ?name, ?nick, ?seeAlso and ?mbox_sha1sum.

We define what these variables mean in the WHERE block. They are triples in the usual standard of subject, predicate and object format. They all need to have the same subject so we'll define a temporary variable called ?x to query against.

We make the query concrete by defining that ?x must have the rdf:type of foaf:Person. We also use the AND block to say that variable ?nick must equal 'Cal'.

We also define the foaf namespace in the USING block.

Putting this all together means we want a selection of triples for the foaf:Person with the nickname of Cal.

We have our query string, so lets actually query our data model.

my $query = new RDF::Redland::Query($string); my $results = $model->query_execute($query);

$results now contains an RDF::Redland::QueryResults object with the results of our RDQL query in. This is basically just a sequence of RDF::Redland::Node's. We need to iterate over this to get each matching set of triples and display them. In our case as there is only one entry for Cal we should only get one set of triples back.

while (!$results->finished) { ## print the triple values out. print node_value($results->binding_value_by_name('name')), "\n"; print node_value($results->binding_value_by_name('nick')), "\n"; print node_value($results->binding_value_by_name('seeAlso')), "\n"; print node_value($results->binding_value_by_name('mbox_sha1sum')), "\n"; ## get the next set of results. $results->next_result; }

We're using the binding_value_by_name method from RDF::Redland::QueryResults to specify which triples we want back. As RDF::Redland::Node's can represent literal values as well as URI's and black nodes, we need to make sure we only get the relevant string back to print. To handle this neatly, we write a subroutine called node_value to return a suitable string value.

sub node_value { my $node = shift; ## if the node is a resource, then return the URI's string value. if ($node->is_resource()) { return $node->uri->as_string; ## if the node is a literal, return the string value. } elsif ($node->is_literal()) { return $node->as_string; ## else return an empty string } else { return ""; } }

Running the code should print to the screen...

Cal Henderson Cal http://www.iamcal.com/foaf.xml 2971b1c2fd1d4f0e8f99c167cd85d522a614b07b

Let's put the code together to produce the final script.

#!/usr/bin/perl -w ## An example showing how to use Redland's Perl bindings to ## extract information from a FOAF file. ## Copyright 2004 - Robert Price - http://www.robertprice.co.uk/ use strict; use RDF::Redland; ## Create storage, in this case we'll be using memory. my $storage = new RDF::Redland::Storage("memory"); die "unable to create storage" unless $storage; ## Create a model using our storage. my $model = new RDF::Redland::Model($storage, ""); die "unable to create model" unless $model; ## We want to access a local copy of our FOAF file, so define that here. my $uri = new RDF::Redland::URI("file:foaf.rdf"); ## create an RDF XML parser as that's the format of our FOAF file. my $parser = new RDF::Redland::Parser("rdfxml", "application/rdf+xml"); die "unable to create parser" unless $parser; ## parse the file and add each triple found to our model. my $stream = $parser->parse_as_stream($uri, $uri); while(!$stream->end) { $model->add_statement($stream->current); $stream->next; } ## build the query and story it in $string. my $string = <<EOF_QUERY; SELECT ?name ?nick ?seeAlso ?mbox_sha1sum WHERE (?x rdf:type foaf:Person), (?x foaf:name ?name), (?x rdfs:seeAlso ?seeAlso), (?x foaf:mbox_sha1sum ?mbox_sha1sum), (?x foaf:nick ?nick) AND (?nick eq 'Cal') USING foaf FOR <http://xmlns.com/foaf/0.1/> EOF_QUERY ## create and run the query, returning a results iterator. my $query = new RDF::Redland::Query($string); my $results = $model->query_execute($query); ## while we have results... while (!$results->finished) { print node_value($results->binding_value_by_name('name')), "\n"; print node_value($results->binding_value_by_name('nick')), "\n"; print node_value($results->binding_value_by_name('seeAlso')), "\n"; print node_value($results->binding_value_by_name('mbox_sha1sum')), "\n"; ## get the next set of results. $results->next_result; } ## Utility subroutine to return a nodes string value. sub node_value { my $node = shift; ## if the node is a resource, then return the URI's string value. if ($node->is_resource()) { return $node->uri->as_string; ## if the node is blank, return an empty string. } elsif ($node->is_literal()) { return $node->as_string; ## else return it's normal string value. } else { return ""; } }

This article will have hopefully shown you how easy it is to query RDF data in Perl and return meaningful results using Redland.

Entered: 2004-10-25 21:54:24
Modified: 2004-11-03 09:38:08
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=434

DMA 04 Runner Up

The DMA 04 - Digital Music Awards took place last night. I was up in the Best Innovation category for the Kiss Radio Player.

Unfortunately the category (which didn't have public voting) was won by DMA 04 sponsors, LAUNCHcast and Yahoo! Messenger Integration.

Coincidence? ;-)

PERMALINK - DMA 04 Runner Up
Entered: 2004-10-20 09:39:11
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=433

Querying RDF In Perl With RDF::Core

Extracting data from an RDF file seems like an easy job for Perl.

I have discussed how to parse RDF in Perl using the RDF::Core module before. In that example I used the getStmts functionality. This is fine for simple things, but it's a bit messy. Thankfully RDF::Core provides us with a query language similar to RDQL to help us get the data we want.

Let's take a quick look at my FOAF file for some example RDF to query. I know Cal Henderson, and this is represented in my FOAF file as...

<foaf:knows> <foaf:Person> <foaf:nick>Cal</foaf:nick> <foaf:name>Cal Henderson</foaf:name> <foaf:mbox_sha1sum>2971b1c2fd1d4f0e8f99c167cd85d522a614b07b</foaf:mbox_sha1sum> <rdfs:seeAlso rdf:resource="http://www.iamcal.com/foaf.xml"/> </foaf:Person> </foaf:knows>

For a detailed explantion of the above RDF, have a look at my previous article - What Is An RDF Triple?

How could I find Cal's foaf file using RDF::Core's query language? It's really simple and we'd use the following query.

select ?x->rdfs:seeAlso, from ?x->foaf:nick{?y} where ?y='Cal'

That just means we want to select the value of the triple rdfs:seeAlso where the parent triple has the foaf:nick of the value of Cal.

Why stop there? Lets get some more information on Cal.

select ?x->foaf:name, ?x->foaf:nick, ?x->rdfs:seeAlso, ?x->foaf:mbox_sha1sum from ?x->foaf:nick{?y} where ?y='Cal'

Now we have Cal's name, nickname, address of his FOAF file and the checksum of his email address.

Let's see how to get this all in Perl now.

We need to setup some RDF::Core objects and parse the FOAF file before we can query it. We'll use this code to do so.

## list of known namespaces we might need. my $namespaces = { 'foaf' => 'http://xmlns.com/foaf/0.1/', 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'dc' => 'http://purl.org/dc/elements/1.1/', }; ## create our model and storage for triples. my $store = RDF::Core::Storage::Memory->new; my $model = RDF::Core::Model->new(Storage => $store); ## create node factory for query my $factory = RDF::Core::NodeFactory->new; ## create our parse and parse the Source file. my $parser = RDF::Core::Model::Parser->new( Model => $model, BaseURI => 'http://xmlns.com/foaf/0.1/', Source => './foaf.rdf', SourceType => 'file' ); $parser->parse;

For a full explanation of the above code, have a look at my previous article - How To Parse RDF In Perl.

We now need to add the new code.

Firstly we need to a RDF::Core::Evaluator and a RDF::Core::Query object. These two objects handle the querying of the data held in our $model.

## create an evaluator based on our model, factory and namespaces. my $evaluator = RDF::Core::Evaluator->new( Model => $model, Factory => $factory, Namespaces => $namespaces, ); ## create a query object based on the evaluator. my $query = RDF::Core::Query->new(Evaluator => $evaluator);

As we have a query object, we can just insert our query statement using it's query method.

## run our query and save the results in $results. my $results = $query->query("select ?x->foaf:name, ?x->foaf:nick, ?x->rdfs:seeAlso, ?x->foaf:mbox_sha1sum from ?x->foaf:nick{?y} where ?y='Cal'");

This executes the query and returns the results as a reference to an array in the $results variable. We need to use the first arrayref returned in $results to get the data we need. This will be a list of RDF::Core::Literal or RDF::Core::Resource objects. As these both inherit from RDF::Core::Node, let's just use that object's getLiteral method to return the string values of the data we need and print it out.

## go over the results and print the data out. foreach my $result (@{$results->[0]}) { print $result->getLabel, "\n"; }

This will return us the following data.

Cal Henderson Cal http://www.iamcal.com/foaf.xml 2971b1c2fd1d4f0e8f99c167cd85d522a614b07b

Here's the final code in all it's glory.

#!/usr/bin/perl -w use strict; use RDF::Core::Evaluator; use RDF::Core::Model; use RDF::Core::Model::Parser; use RDF::Core::NodeFactory; use RDF::Core::Query; use RDF::Core::Resource; use RDF::Core::Storage::Memory; ## list of known namespaces we might need. my $namespaces = { 'foaf' => 'http://xmlns.com/foaf/0.1/', 'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 'dc' => 'http://purl.org/dc/elements/1.1/', }; ## create our model and storage for triples. my $store = RDF::Core::Storage::Memory->new; my $model = RDF::Core::Model->new(Storage => $store); ## create node factory for query my $factory = RDF::Core::NodeFactory->new; ## create our parse and parse the Source file. my $parser = RDF::Core::Model::Parser->new( Model => $model, BaseURI => 'http://xmlns.com/foaf/0.1/', Source => './foaf.rdf', SourceType => 'file' ); $parser->parse; ## create an evaluator based on our model, factory and namespaces. my $evaluator = RDF::Core::Evaluator->new( Model => $model, Factory => $factory, Namespaces => $namespaces, ); ## create a query object based on the evaluator. my $query = RDF::Core::Query->new(Evaluator => $evaluator); ## run our query and save the results in $results. my $results = $query->query("select ?x->foaf:name, ?x->foaf:nick, ?x->rdfs:seeAlso, ?x->foaf:mbox_sha1sum from ?x->foaf:nick{?y} where ?y='Cal'"); ## go over the results and print the data out. foreach my $result (@{$results->[0]}) { print $result->getLabel, "\n"; }

If you see warnings (when running using -w or use warnings;) from RDF::Core about use of an uninitialised value before the results are printed don't worry. This is just a small bug in RDF::Core and won't affect the running of the code.

Entered: 2004-10-17 12:23:38
Modified: 2004-10-25 21:55:22
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=432

Parsing RDF In Perl With RDF::Core

It is often said that parsing RDF files is hard. It's not. What most people really find hard is turning the RDF/XML into a series of triples and extracting data from them.

When stored in RDF/XML, triples can be written in a variety of various ways, but still mean the same thing. This means using an XML parser may not always give you the same results. To solve this we use an RDF parser to get the triples for us. An RDF parser is usually built on top of an existing XML parser but with enough logic to know how to turn the data contained in an XML file into a list of triples correctly.

There are various RDF parsers for Perl. These include RDF::Core, RDF::Simple, RDF::Redland and several others.

Parsing is all well and good, each one of the above can give us a list of triples. What we really need is a way to query the data to extract the data we need.

In this article I'm going to use RDF::Core.

RDF::Core is a bit of a beast and doesn't have a very Perl'ish interface.

I'm going to parse my FOAF file and extract a list of my friends FOAF files.

FOAF means Friend of a Friend and it allows you to describe relationships between people in a machine readable RDF format. Details of it can be found on the FOAF project website.

So how do we do this using RDF::Core?

Firstly we need to set it up. We'll need to a model and define the storage method we want. RDF::Core allows data to be stored in a database, DB_File or memory. In this example I'll just use memory as it's the simplest.

my $store = RDF::Core::Storage::Memory->new; my $model = RDF::Core::Model->new(Storage => $store;

Now we need create a parser, and parse the the RDF file. To do this we pass in our Model, BaseURI, Source, SourceType of the RDF data. In this example I'm going to use the file foaf.rdf so I'll have to set the SourceType parameter to file. The BaseURI is used to resolve relative URI's, so we'll use the FOAF namespace URI http://xmlns.com/foaf/0.1/ for this. Once we have our parser built, we just call it's parse method.

my $parser = RDF::Core::Model::Parser->new( Model => $model, BaseURI => 'http://xmlns.com/foaf/0.1/', Source => './foaf.rdf', SourceType => 'file' ); $parser->parse;

We now have a list of triples in our memory store. That's great, but we want to find out if any of my friends have FOAF files.

If you look at the example in my previous article on RDF triples, you'll know that my friends details are referenced by the #knows predicate. So we need to find out all the triples that have that as a predicate and get their objects. Once we have these, we can look for all triples that have this as their subject and also have the predicate of #seeAlso. The object of all these triples will be the address of my friend's FOAF files.

Refer to my article What Is An RDF Triple for a detailed explanation and commented example of the above.

RDF::Core needs all resources to be created as RDF::Core::Resource objects. We'll need to create resources for #seeAlso and #knows if we are to query with them, so lets do that now.

my $seealso = RDF::Core::Resource->new('http://www.w3.org/2000/01/rdf-schema#seeAlso'); my $knows = RDF::Core::Resource->new('http://xmlns.com/foaf/0.1/knows');

We can use the getStmts method in the model to get triples matching a given subject, predicate or object. We can either pass in RDF::Core::Resource's with the values we want to check against, or undef if we want to match everything. This returns an enumerator that we can use to get each matching triple in turn.

To start with we need to get a list of any triples that match our #knows predicate. This is done like this.

my $knows_enum = $model->getStmts(undef, $knows, undef);

Next we need to enumerate over any triples we have.

my $statement = $knows_enum->getFirst; while (defined $statement) { my $knows_object = $statement->getObject; ## code to handle each statement goes here $statement = $knows_enum->getNext; }

Now we know have a list of triples whose object represents the subject of the triples we want to query, We need to match those with this value as the subject and with the predicate of #seeAlso.

We get get the triples using code very similar to code just used. If we insert the following code into the while loop, we can extract this information.

my $seealso_enum = $model->getStmts($knows_object, $seealso, undef); my $seealso_object = $seealso_enum->getFirst; if (defined $seealso_object) { print $seealso_object->getObject->getLabel, "\n"; }

As we only wanted the first #seeAlso value per #knows triple, we don't have to worry about going through every object in the enumerator, only the first.

Putting it all together, we have a simple script to parse a FOAF file, and print out address of our friend's FOAF files.

#!/usr/bin/perl -w ## Extract a list of seeAlso triples from a FOAF file ## Robert Price - http://www.robertprice.co.uk/ use strict; use RDF::Core::Model; use RDF::Core::Storage::Memory; use RDF::Core::Model::Parser; use RDF::Core::Resource; ## create our model and storage for triples. my $store = RDF::Core::Storage::Memory->new; my $model = RDF::Core::Model->new(Storage => $store); ## create our parse and parse the Source file. my $parser = RDF::Core::Model::Parser->new( Model => $model, BaseURI => 'http://xmlns.com/foaf/0.1/', Source => './foaf.rdf', SourceType => 'file' ); $parser->parse; ## create a resource for seeAlso and knows triples. my $seealso = RDF::Core::Resource->new('http://www.w3.org/2000/01/rdf-schema#seeAlso'); my $knows = RDF::Core::Resource->new('http://xmlns.com/foaf/0.1/knows'); ## create an enumerator with all the knows triples. my $knows_enum = $model->getStmts(undef, $knows, undef); ## enumerate over each knows triple. my $statement = $knows_enum->getFirst; while (defined $statement) { ## get the object of the current triple. my $knows_object = $statement->getObject; ## look for subject of the enumerator (knows), and predicate of ## seealso my $seealso_enum = $model->getStmts($knows_object, $seealso, undef); my $seealso_obj = $seealso_enum->getFirst; ## if it has a seealso triple, show the value of it. if (defined $seealso_obj) { print $seealso_obj->getObject->getLabel, "\n"; } ## get the next knows statement. $statement = $knows_enum->getNext; }

I hope this has started to demystify RDF parsing with Perl.

Entered: 2004-10-07 08:55:04
Modified: 2004-10-25 21:55:40
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=431

Serving MS Excel Documents From A Perl CGI Script

How can you serve a Microsoft Excel spreadsheet from a Perl CGI script?

It's actually quite easy, and just a case of sending the right headers.

Assuming we have our raw binary Excel sheet in a variable called $excel, we can use this simple block of code to allow it to be downloaded from a web script with the filename of text.xls.

print "content-type: application/vnd.ms-excel\n"; print "content-disposition: attachment; filename=text.xls\n\n"; print $excel;

This will prompt the user with a file download box asking where to store text.xls.

The key here is the content-disposition tag. We could change the content-disposition to inline to try to force the browser to open the Excel document in browser itself, but that's not really very friendly.

Entered: 2004-10-06 17:19:29
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=430

Tube Revenue Protection Officers

For the first time since I started working in London 6 and a half years ago, I've finally seen revenue protection officers on The Tube.

They managed to catch a fare dodger at the end of the carriage too.

Unfortunately I don't think that his £10 penalty fare will make much difference to London's £3 billion pound transport funding blackhole.

Tube Revenue Protection Officers On The Central Line

Entered: 2004-10-05 09:37:12
Modified: 2004-10-05 09:37:57
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=429

At The Q Awards 2004 Aftershow Party

I went to the Q Awards aftershow party last night.

It was at Rouge on Charing Cross Road.

It what seemed like a storyline from Alan Partridge, food was provided by Ginsters pasties, the celebs favourite.

Unfortunately the awards themselves had been held earlier in the day elsewhere, so the big names like U2, Elton John, etc, never came along after.

Q Menu - Sponsored by Ginsters

Q Awards 2004 - photo 1

Q Awards 2004 - photo 2

Q Awards 2004 - photo 3

Entered: 2004-10-05 09:18:43
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=428

Nomiated For A 2004 DMA Award

Digital Music Awards 2004 The Kiss 100 Radio Player I built has been nominated for a DMA Award.

We're nominated in the Best Innovation category, and face stiff competition from Hyperteams, Yahoo Messenger & LAUNCHcast, Universal Streamed Media Centre, and Robbie Williams Cinecast.

The Radio Player lets you listen to Kiss Radio on demand. All the specialist shows from the past week are archived and available from our streaming server.

Apparently the awards are designed to bring out the best of cutting edge music entertainment and celebrate those who have done most to reach music fans using Digital Media.

Unfortunately the Best Innovation category doesn't have online voting, but most of the others do.

Kiss Radio Player

Entered: 2004-10-04 14:41:27
Modified: 2004-10-04 14:42:08
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=427

Faulty Bendy Bus

Looks like one of the reliable London bendy buses has broken down.

This one was snapped on Regent Street. The back of the bus was open with the engine exposed, so I hope it had just overheated and wasn't about to explode.

Broken Down Bendy Bus

PERMALINK - Faulty Bendy Bus
Entered: 2004-10-04 14:17:00
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=426

What Is An RDF Triple?

An RDF file should parse down to a list of triples.

A triple consists of a subject, a predicate, and an object. But what do these actually mean?

The subject is, well, the subject. It identifies what object the triple is describing.

The predicate defines the piece of data in the object we are giving a value to.

The object is the actual value.

Let's take a quick look at my FOAF file to get an example triple.

I know Cal Henderson, and this is represented in my FOAF file as...

<foaf:knows> <foaf:Person> <foaf:nick>Cal</foaf:nick> <foaf:name>Cal Henderson</foaf:name> <foaf:mbox_sha1sum>2971b1c2fd1d4f0e8f99c167cd85d522a614b07b</foaf:mbox_sha1sum> <rdfs:seeAlso rdf:resource="http://www.iamcal.com/foaf.xml"/> </foaf:Person> </foaf:knows>

Using the RDF validator we can get a the list of triples represented in this piece of RDF.

Triple Subject Predicate Object
1 genid:ARP40722 http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://xmlns.com/foaf/0.1/Person
2 genid:ARP40722 http://xmlns.com/foaf/0.1/nick "Cal"
3 genid:ARP40722 http://xmlns.com/foaf/0.1/name "Cal Henderson"
4 genid:ARP40722 http://xmlns.com/foaf/0.1/mbox_sha1sum "2971b1c2fd1d4f0e8f99c167cd85d522a614b07b"
5 genid:ARP40722 http://www.w3.org/2000/01/rdf-schema#seeAlso http://www.iamcal.com/foaf.xml
6 genid:me http://xmlns.com/foaf/0.1/knows genid:ARP40722

But what does this actually mean? Lets go through it line by line.

Triple 1 - This has a subject of genid:ARP40722. You would have noticed that this didn't appear in our original RDF, so where did it come from? It's actually a bnode, and the value is generated by the RDF Validator. It's purpose is to make sure we can identify the subject where it hasn't been specifically named. In this case, we look to triple 6 and see that it has been generated there as the value of that object. The predicate of line 1 is the rdf:type, and the object is foaf:Person. This all corresponds to the code in <foaf:Person> in our FOAF extract.

Triple 2 - You'll notice this has the same subject as triple 1. This isn't a coincidence as the triple is part of the same foaf:Person. The predicate says we are defining the foaf:nick property, and the object is Cal. So we know that this foaf:Person has a nickname of Cal. This all corresponds to the line <foaf:nick>Cal</foaf:nick> in our FOAF extract.

Triple 3 - Yet again the subject is the same as triple 1. The predicate is foaf:name, and the object is Cal Henderson. So we know the name of the person in this foaf:Person is Cal Henderson. This represents the line <foaf:name>Cal Henderson</foaf:name>

Triple 4 - Yet again the subject is the same as triple 1. The predicate is foaf:mbox_sha1sum and the object is the SHA1 sum relating to Cal's email address. This represents the line <foaf:mbox_sha1sum>2971b1c2fd1d4f0e8f99c167cd85d522a614b07b</foaf:mbox_sha1sum>

Triple 5 - Yet again the subject is the same. The predicate is rdfs:seeAlso and the object is http://www.iamcal.com/foaf.xml. We now know that if we want more information on Cal, we can see his FOAF file at that URL. This represents the line <rdfs:seeAlso rdf:resource="http://www.iamcal.com/foaf.xml"/>

Triple 6 - Here the subject is genid:me. This is because at the start of my FOAF file, in an area not show above I defined the id as me. The predicate is foaf:knows and the object is genid:ARP40722. This is saying that I know the person defined by the subject genid:ARP40722. In this case, it's Cal, and his details have been shown in the previous 5 triples. This relates to the <foaf:knows> block.

Hopefully this has shown how the RDF has been parsed into triples, and how they relate to each other.

Shelley Powers, in her excellent book Practical RDF, helpfully describes triples as the following.

  • Each RDF triple is made up of subject, predicate and object.
  • Each RDF triple is a complete and unique fact.
  • An RDF triple is a 3-tuple, which is made up of a subject, predicate and object - which are respectively a uriref or bnode; a uriref; and a uriref, bnode or literal.
  • Each RDF triple can be joined with other RDF triples, but it still retains its own unique meaning, regardless of the complexity of the models in which it is included.

Entered: 2004-10-01 08:44:54
Modified: 2004-10-25 21:56:06
TRACKBACK - http://www.robertprice.co.uk/cgi-bin/robblog/trackback.pl?id=425