#!/usr/bin/perl
#######################################################################
package Local::Database::ToRDF;
use 5.008;
use strict;
use DBI;
use RDF::Trine qw[statement iri blank literal];
use RDF::Trine::Namespace qw[rdf rdfs owl xsd];
sub new
{
my ($class, %mappings) = @_;
bless {%mappings}, $class;
}
sub namespaces
{
my ($self) = @_;
my %NS;
%NS = %{ $self->{-namespaces} }
if ref $self->{-namespaces} eq 'HASH';
%NS = (
owl => "$owl",
rdf => "$rdf",
rdfs => "$rdfs",
xsd => "$xsd",
) unless %NS;
return %NS;
}
sub process
{
my ($self, $dbh, $model) = @_;
$model = RDF::Trine::Model->temporary_model unless defined $model;
my $parsers = {};
my %NS = $self->namespaces;
TABLE: while (my ($table, $tmap) = each %$self)
{
next if $table =~ /^-/;
# ->{select}
my $select = $tmap->{select} || '*';
my $sql = "SELECT $select FROM $table";
my $sth = $dbh->prepare($sql);
$sth->execute;
ROW: while (my $row = $sth->fetchrow_hashref)
{
my %row = %$row;
# ->{about}
my $subject;
if ($tmap->{about})
{
$tmap->{about} = [$tmap->{about}] unless ref $tmap->{about} eq 'ARRAY';
my ($template, @cols) = @{ $tmap->{about} };
$subject = iri(sprintf($template, @row{@cols}));
}
$subject ||= RDF::Trine::Node::Blank->new;
# ->{typeof}
foreach (@{ $tmap->{typeof} })
{
$_ = iri($_) unless ref $_;
$model->add_statement(statement($subject, $rdf->type, $_));
}
# ->{columns}
my %columns = %{ $tmap->{columns} };
COLUMN: while (my ($column, $list) = each %columns)
{
MAP: foreach my $map (@$list)
{
my $predicate;
my $value = $row{$column};
if (defined $map->{parse} and uc $map->{parse} eq 'TURTLE')
{
next MAP unless length $value;
my $turtle = join '', map { sprintf("\@prefix %s: <%s>.\n", $_, $NS{$_}) } keys %NS;
$turtle .= sprintf("\@base <%s>.\n", $subject->uri);
$turtle .= "$value\n";
eval {
$parsers->{ $map->{parse} } = RDF::Trine::Parser->new($map->{parse});
$parsers->{ $map->{parse} }->parse_into_model($subject, $turtle, $model);
};
next MAP;
}
if ($map->{rev} || $map->{rel})
{
if ($map->{resource})
{
$value = sprintf($map->{resource}, $value);
}
$predicate = $map->{rev} || $map->{rel};
$value = iri($value);
}
elsif ($map->{property})
{
if ($map->{content})
{
$value = sprintf($map->{content}, $value);
}
$predicate = $map->{property};
$value = literal($value, $map->{lang}, $map->{datatype});
}
if (defined $predicate)
{
$predicate = iri($predicate) unless ref $predicate;
my $lsubject = $subject;
if ($map->{about})
{
$map->{about} = [$map->{about}] unless ref $map->{about} eq 'ARRAY';
my ($template, @cols) = @{ $map->{about} };
$lsubject = iri(sprintf($template, @row{@cols}));
}
if ($map->{rev})
{
$model->add_statement(statement($value, $predicate, $lsubject));
}
else
{
$model->add_statement(statement($lsubject, $predicate, $value));
}
}
}
}
}
}
return $model;
}
#######################################################################
package main;
use 5.008;
use strict;
use DBI;
use RDF::Trine;
use RDF::Trine::Namespace qw[rdf rdfs owl xsd];
my $foaf = RDF::Trine::Namespace->new('http://xmlns.com/foaf/0.1/');
my $bibo = RDF::Trine::Namespace->new('http://purl.org/ontology/bibo/');
my $dc = RDF::Trine::Namespace->new('http://purl.org/dc/terms/');
my $skos = RDF::Trine::Namespace->new('http://www.w3.org/2004/02/skos/core#');
my $mapping = Local::Database::ToRDF->new(
-namespaces => {
bibo => "$bibo",
dc => "$dc",
foaf => "$foaf",
rdfs => "$rdfs",
skos => "$skos",
},
books => {
about => ['http://example.net/id/book/%d', 'book_id'],
typeof => [$bibo->Book],
columns => {
title => [{property => $rdfs->label, lang=>'en'}, {property => $dc->title, lang=>'en'}],
turtle => [{parse => 'Turtle'}],
},
},
authors => {
select => "*, forename||' '||surname AS fullname",
about => ['http://example.net/id/author/%d', 'author_id'],
typeof => [$foaf->Person],
columns => {
forename => [{property => $foaf->givenName}],
surname => [{property => $foaf->familyName}],
fullname => [{property => $rdfs->label}, {property => $foaf->name}],
turtle => [{parse => 'Turtle'}],
},
},
topics => {
about => ['http://example.net/id/topic/%d', 'topic_id'],
typeof => [$skos->Concept],
columns => {
label => [{property => $rdfs->label, lang=>'en'}, {property => $skos->prefLabel, lang=>'en'}],
turtle => [{parse => 'Turtle'}],
},
},
book_authors => {
about => ['http://example.net/id/book/%d', 'book_id'],
columns => {
author_id=> [{rel => $dc->creator, resource => 'http://example.net/id/author/%d'}, {rel => $foaf->maker, resource => 'http://example.net/id/author/%d'}, {rev => $foaf->made, resource => 'http://example.net/id/author/%d'}, {rel => $bibo->author, resource => 'http://example.net/id/author/%d'}],
},
},
book_topics => {
about => ['http://example.net/id/book/%d', 'book_id'],
columns => {
topic_id => [{rel => $dc->subject, resource => 'http://example.net/id/topic/%d'}],
},
},
);
my $file = shift @ARGV;
my $dbh = DBI->connect("dbi:SQLite:dbname=$file");
my $model = $mapping->process($dbh);
print RDF::Trine::Serializer
->new('Turtle', namespaces => { $mapping->namespaces })
->serialize_model_to_string($model);