Monday, February 16, 2009

RDF/XML and C#.NET

RDF (Resource Description Framework) is the standard for the Semantic Web. RDF can be mixed with C#.NET in an open source project called the semantic web at http://razor.occams.info/code/semweb/ . As stated by the authors, SemWeb is a

"... library can be used for reading and writing RDF (XML, N3), keeping RDF in persistent storage (memory, MySQL, etc.), querying persistent storage via simple graph matching and SPARQL, and making SPARQL queries to remote endpoints. Limited RDFS and general-purpose inferencing is also possible. SemWeb's API is straight-forward and flexible".

A basic "Hello World" application using SemWeb is

using System;
using SemWeb;

public class Example
{
const string RDF = http://www.w3.org/1999/02/22-rdf-syntax-ns#;

public static void Main() {
MemoryStore store = new MemoryStore();
Entity computer = new Entity("http://example.org/computer");
Entity says = "http://example.org/says";
Entity wants = "http://example.org/wants";
Entity desire = new BNode();
Entity description = new Entity("http://example.org/description");
store.Add(new Statement(computer, says, (Literal)"Hello world!"));
store.Add(new Statement(computer, wants, desire));
store.Add(new Statement(desire, description, (Literal)"to be human"));
store.Add(new Statement(desire, RDF+"type", (Entity)"http://example.org/Desire"));
using (RdfWriter writer = new RdfXmlWriter(Console.Out)) { writer.Namespaces.AddNamespace("http://example.org/", "ex");
writer.Write(store);
}
}
}

A discussion on this example and others can be found at http://razor.occams.info/code/semweb/semweb-current/doc/helloworld.html. Another example of a Semantic Web framework for .NET is LinqToRDF at http://code.google.com/p/linqtordf/. Andrew Matthews has an excellent LinqToRDF tutorial which shows how to create an ontology, link the ontology to .NET and query the ontology using SPARSQL. Creation of an ontology begins with XML namespaces for OWL, RDF and XML:


@prefix rdf: .
@prefix daml: .
@prefix log: .
@prefix rdfs: .
@prefix owl: .
@prefix xsdt: .
@prefix : .


followed by some classes for the ontology:


:Album a owl:Class.
:Track a owl:Class.
:title rdfs:domain :Track;
rdfs:range xsdt:string.
:artistName
rdfs:domain :Track;
rdfs:range xsdt:string.
:albumName
rdfs:domain :Track;
rdfs:range xsdt:string.
:year
rdfs:domain :Album;
rdfs:range xsdt:integer.
:genreName
rdfs:domain :Track;
rdfs:range xsdt:string.
:comment
rdfs:domain :Track;
rdfs:range xsdt:string.
:isTrackOn
rdfs:domain :Track;
rdfs:range :Album.
:fileLocation
rdfs:domain :Track;
rdfs:range xsdt:string.

Having used Linq in .NET 3.5 in VS.NET 2008, the following class Track links to the above ontology

using LinqToRdf;
namespace RdfMusic
{
[OwlResource(OntologyName="Music", RelativeUriReference="Track")]
public class Track : OwlInstanceSupertype
{
[OwlResource(OntologyName = "Music",
RelativeUriReference = "title")]
public string Title { get; set; }
[OwlResource(OntologyName = "Music",
RelativeUriReference="artistName")]
public string ArtistName { get; set; }
[OwlResource(OntologyName = "Music",
RelativeUriReference="albumName")]
public string AlbumName { get; set; }
[OwlResource(OntologyName = "Music",
RelativeUriReference="year")]
public string Year { get; set; }
[OwlResource(OntologyName = "Music",
RelativeUriReference="genreName")]
public string GenreName { get; set; }
[OwlResource(OntologyName = "Music",
RelativeUriReference="comment")]
public string Comment { get; set; }
[OwlResource(OntologyName = "Music",
RelativeUriReference="fileLocation")]
public string FileLocation { get; set; }
[OwlResource(OntologyName = "Music",
RelativeUriReference="rating")]
public int Rating { get; set; }
public Track(TagHandler th, string fileLocation)
{
FileLocation = fileLocation;
Title = th.Track;
ArtistName = th.Artist;
AlbumName = th.Album;
Year = th.Year;
9
GenreName = th.Genere;
Comment = th.Comment;
}
private EntityRef _Album { get; set; }
[OwlResource(OntologyName = "Music",
RelativeUriReference = "isTrackOn")]
public Album Album
{
get
{
if (_Album.HasLoadedOrAssignedValue)
return _Album.Entity;
if (DataContext != null)
{
var ctx = (MusicDataContext)DataContext;
string trackUri = this.InstanceUri;
string trackPredicateUri =
this.PredicateUriForProperty(MethodBase.GetCurrentMethod());
_Album = new EntityRef(
from r in ((MusicDataContext)DataContext).Albums
where r.StmtObjectWithSubjectAndPredicate(trackUri,
trackPredicateUri)
select r);
return _Album.Entity;
}
return null;
}
}
public Track()
{
}
}

One of the nice features of the open source C# semWeb is the RDFS inferencing engine with

using System;
using System.IO;
using SemWeb;
using SemWeb.Inference;

public class EulerTest {
public static void Main() {
// Create the instance data
MemoryStore dataModel = new MemoryStore();
BNode me = new BNode("me");
BNode you = new BNode("you");
Entity rdfType = http://www.w3.org/1999/02/22-rdf-syntax-ns#type;
Entity rdfsLabel= http://www.w3.org/2000/01/rdf-schema#label;
Entity foafPerson = http://xmlns.com/foaf/0.1/Person;
Entity foafAgent = http://xmlns.com/foaf/0.1/Agent;
Entity foafName = http://xmlns.com/foaf/0.1/name;
dataModel.Add(new Statement(me, rdfType, foafPerson));
dataModel.Add(new Statement(you, rdfType, foafPerson));
dataModel.Add(new Statement(me, foafName, (Literal)"John Doe"));
dataModel.Add(new Statement(you, foafName, (Literal)"Sam Smith"));
// Create the RDFS engine and apply it to the data model.
RDFS engine = new RDFS();
engine.LoadSchema(RdfReader.LoadFromUri(new Uri(http://xmlns.com/foaf/0.1/index.rdf)));
dataModel.AddReasoner(engine);
// Query the data model
// Ask for who are typed as Agents.
Note that the people are
// typed as foaf:Person, and the schema asserts that foaf:Person
// is a subclass of foaf:Agent.
Console.WriteLine("Who are Agents?");
foreach (Entity r in dataModel.SelectSubjects(rdfType, foafAgent))
Console.WriteLine("\t" + r);
// Ask for the rdfs:labels of everyone. Note that the data model
// has foaf:names for the people, and the schema asserts that
// foaf:name is a subproperty of rdfs:label.
Console.WriteLine("People's labels:");
foreach (Statement s in dataModel.Select(new Statement(null, rdfsLabel, null))) Console.WriteLine("\t" + s);
}
}

In addition, there is backward-forward-backward chaining reasoning with Euler path detection-"don't step in your own steps". For more information on Euler, look at http://www.agfa.com/w3c/euler/. Thus, in the construction of intelligent autonomous Agent based models in C#, there are opportunities to implement rule based engines with ontologies.

1 comment:

Anonymous said...

Hello Jeff,

I have been using SemWeb for quite a while and I am quite impressed with it. It sticks to RDF triple level of programming and it does that well. However, when you start dealing with ontologies and business objects, our belowed triples can become a burden and we might rather want them to be hidden and automatically taken care of. For such a tasks, I found ROWLEX to be suitable. From your ontology, you can generate corresponding C# classes and have the triples handled automatically. It also allows you to add serialization attributes to your .NET business classes and serialize you business objects into RDF similarly as you would do with XmlSerializer.

Cheers,

RollerBoy