Why RDF Templates Uses Macros

I'm working on a .NET version of RDF Templates using Carp. James asked why you had to use macros to get nice NodePaths like *[rdf:type = foaf:Agent] instead of QNames based off of the namespaces in the source documents or stylesheet. It's been a while since I wrote the specification and to be honest I'd forgotten why, other than just disliking QNames in content.

After a bit of thought I remembered some of the original reasoning. You couldn't base QName expansion off of the source documents because RDF Templates and their NodePaths operate over a graph, not an instance document. The graph could have been bult from hundreds of individual instance documents each with their own namespace prefix mappings (or none if the RDF source wasn't XML based).

That leaves getting QName mappings from the stylesheet. One reason why this would be a bad idea is that it would force the stylesheet author to include namespace mappings for every QName they refer to in their NodePaths even if they never use an element from that namespace in their actual markup. Roundtripping the stylesheet through an XML processor then becomes problematic since many processors will not output unused namespace declarations and of course the only usage in the stylesheet might hidden in a NodePath inside an attribute value.

The last part of the reasoning is that macros give a lot more expressive power than QNames. A QName by definition can only represent a namespace URI and local name. A macro on the other hand can represent almost any part of a NodePath. They can contain other macros too, so you can simplify very complex expressions down to a few concise macros and get a lot of reuse througout the document. A future version of RDF Templates will also allow inclusion of stylesheets which would enable the creation of macro libraries, essentially libraries of useful RDF query expressions.

I knew there was a good reason why I avoided QNames…

Announcing Giblet

Giblet (Graph In-Browser Lightweight Editing Tool) allows the creation of simple RDF graphs, based on available RDF schemata. It runs as a Javascript application in an enabled browser.

Launch Giblet.

This experimental release of Giblet is targetted at RDF savvy developers, although we will re-skin it with a less geeky look and feel for beginner tasks like FOAF or DOAP generation. There is a constant tension here between power and simplicity.

Giblet offers the following basic features:

  • Class selection and resource creation
  • Appropriate property selection, based on the domain of the class and its super-classes
  • Appropriate value input, whether as a string literal or an internal resource reference (based on the range of the property)
  • Saving and loading of simple graphs to and from local storage, via cookies
  • Export of graphs as RDF/XML, and a submission to the W3C's online image generator for RDF graphs
  • Availability of templates (initially FOAFNet only) to help a user get started

Giblet is initialised by a model described in Javascript. This is generated from various RDF schema by XSLT, although we will use Semantic Planet's CARP library in future. Giblet has been tested in FireFox preview release 1 and Internet Explorer 6 only. If you use it with another browser, please let us know.

Future plans for Giblet, including the dynamic loading of schemata, can be found on the Giblet wiki page.

We'd love Giblet to get used. If it does not do exactly what you need, or you feel it could be improved, please let me know by e-mailing me at james dot carlyle at semanticplanet.com.

Site Redesign

If you can read this then your DNS has picked up our server change. We've also given the site a new look, retired some areas that were past their sell-by date and dusted off a few things that were lurking in the background.

Announcing Semantic Planet's RDF Lib and Carp

James and I are pleased to announce the first public release of RdfLib and Carp, which are .NET/Mono libraries for fetching, parsing, munging and writing RDF. I first mentioned the existence of these in my talk at FOAF Galway and both are used to run our FriendSpace experiment. Both libraries are released under a liberal, attribution-only, open source license. We'd love it if you wanted to use either or both in your applications. If you do use them, please let us know - we're keen to help as much as possible.

SemPlan.RdfLib (binaries / source / docs) provides foundation RDF services for other applications such as parsing and writing RDF. The SemPlan.RdfLib.Core namespace contains interfaces defining fundamental RDF concepts such as UriRef, BlankNode etc. It also contains a Parser interface with a number of associated Factory interfaces to get hold of parsers, resources and statements.

We ship three parsers: XsltParser which is based off of James’ XSL stylesheet RDF parser; DriveParser which is a wrapper around the pure .NET Drive Parser and ICalParser which wraps SemaView's iCal parser. All of these implement the same interface and so are completely interchangeable. It should be very little effort to produce other RDF parser bindings.

The SemPlan.RdfLib.Utility namespace contains some handy implementations of the RdfWriter interface (RdfXmlWriter and NTripleWriter), plus a SimpleModel and SimpleDereferencer, both of which are suitable for quick hacking.

SemPlan.Carp (binaries / source / docs) uses RdfLib and conceptually sits in a layer above it. Carp stands for Convenient API for RDF Programing and is designed to provide a simple API for programming with RDF without losing the power of the underlying model.

The heart of Carp is the KnowledgeBase. You can include RDF from abitrary locations very simply:

KnowledgeBase knowledge = new KnowledgeBase();
knowledge.include("http://www.semanticplanet.com/index.rdf");
knowledge.include( new StreamReader("./bloggers.rdf"), "");

By default, a KnowledgeBase uses the XsltParser but you can supply any ParserFactory in its constructor to customise that behaviour. In Carp, one of the design principles is to make simple things really simple and hard things possible so most constructors and quite a few methods offer overloaded versions with sensible defaults.

The KnowledgeBase maintains a ResourceDescription for every node in the input documents. A ResourceDescription is a set of properties and values attached to a uriref or a blank node. Each value is a further ResourceDescription with its own properties and values. You can ask any KnowledgeBase for a description of a node like this:

ResourceDescription description = knowledge.getDescriptionOf("http://www.semanticplanet.com/");

Or, you can iterate through all the descriptions:

foreach( ResourceDescription description in knowledge) {
  Console.WriteLine( description.getAboutLabel() );
}

The KnowledgeBase also offers some basic, but useful, querying methods such as findByPropertyValue and findByType. You can also write out the KnowledgeBase as RDF by passing an RdfWriter to its write method. Here's a FOAF aggregator:

KnowledgeBase knowledge = new KnowledgeBase();
knowledge.include("http://iandavis.com/foaf.rdf");
knowledge.include("http://www.takepart.com/about/foaf.rdf");
    
RdfXmlWriter writer = new RdfXmlWriter( new XmlTextWriter( Console.Out ) );
knowledge.write( writer );    

The ResourceDescription class is probably the next most important after KnowledgeBase. Once you've got hold of one you can get the values of its properties using the [ ] indexer notation. Property values are always returned as a ResourceDescriptionList which you can enumerate with foreach, index into with [ ] notation or just use the first() method to get the first value. For example:

ResourceDescriptionList topics = doc["http://xmlns.com/foaf/0.1/topic"];
foreach( ResourceDescription topic in topics ) {
  Console.WriteLine( topic.getAbout().getLabel() );
}

or, more succinctly,

foreach( ResourceDescription topic in doc["http://xmlns.com/foaf/0.1/topic"] ) {
  Console.WriteLine( topic );
}

Calling WriteLine with a ResourceDescription argument like above invokes ToString on the ResourceDescription. This, in turn, returns the label of the node the ResourceDescription is about. The label is either the uri of a uriref, a generated blank node label, or the string value of any literal.

Setting properties is as easy as getting them for a ResourceDescription:

ResourceDescription person = new ResourceDescription();
person["http://xmlns.com/foaf/0.1/name"] += "Ian Davis";
person["http://xmlns.com/foaf/0.1/interest"] += new ResourceDescription( new UriRef("http://example.com"));

Note that in the spirit of RDF we're adding values to a list of values for each of the properties. Copying from one ResourceDescription to another is easy too:

ian["http://xmlns.com/foaf/0.1/interest"] += james["http://xmlns.com/foaf/0.1/interest"];

There's a lot more to core Carp than just these two classes such as investigators that encapsulate algorithms for discovering more RDF about resources, a caching dereferencer with etag and if-modified-since support, and an early implementation of RDF Schema inference rules. However, the I want to finish by describing the SemPlan.Carp.Vocabularies namespace. This, as you might guess, contains classes for using common RDF vocabularies. Carp comes with FOAF, RSS 1.0, RDFS and a partial RdfCal implementation. If you want more, there's an example application in the source distribution called VocabGenerator that can generate C# classes compatible with Carp from RDF Schemas.

To use a Carp vocabulary class, just add using SemPlan.RdfLib.Vocabularies; to your class. Then you can address any FOAF property by its name:

ResourceDescription person = new ResourceDescription();
person[ Foaf.name ] += "Ian Davis";
person[ Foaf.interest ] += new ResourceDescription( new UriRef("http://example.com"));

The vocabulary classes also provide a typed form of ResourceDescription for each class defined in the schema. Each typed ResourceDescription has shortcut properties inferred from the schema that make it even easier to manipulate RDF data:

foreach( Foaf.Person person in james.knows) ) {
  Console.WriteLine( person.name + " (" + person.mbox + ")" );
}

Typed ResourceDescription have their own collections, addressed as Agent.Collection and contain implicit casts to and from ordinary ResourceDescriptions and ResourceDescriptionLists. Creating a new typed ResourceDescription creates a pattern which you can use to query the knowledge base for all nodes of that type.

Foaf.Agent pattern = new Foaf.Agent();
foreach (Foaf.Agent agent in pattern.findAllMatching( knowledge ) ) {
 ... do something with agent
}

I'll leave you for now with one of examples, an RSS aggregator that uses the same format of blogroll as Planet RDF:

KnowledgeBase bloggers = new KnowledgeBase(parserFactory);
bloggers.include(new StreamReader("./bloggers.rdf"), "");
Foaf.Agent pattern = new Foaf.Agent();

foreach (Foaf.Agent agent in pattern.findAllMatching( bloggers ) ) {
  Console.WriteLine( agent[Foaf.name] );
  foreach (Foaf.Document doc in agent.weblog) {
    Console.WriteLine( "Weblog: " + doc );

    KnowledgeBase rss = new KnowledgeBase();
    rss.investigate( doc , new SeeAlsoPropertyInvestigator());

    Rss.Item itemPattern = new Rss.Item();
    foreach (Rss.Item item in itemPattern.findAllMatching( rss ) ) {
      Console.WriteLine( "  - " + item.title );
      Console.WriteLine( "    " + item.description );
    }
  }          
}