Wednesday, January 17, 2007

Jena tip: get the OWL class of a Resource or Individual

This is a frequently asked question that I'm going to address here and feed to future seekers after truth by the power of Google. In an OWL ontology (or, equivalently RDFS or DAML, but I'm just going to talk about OWL here for simplicity), you might find something like this:

<ex:Dog rdf:ID="deputy_dawg">
  <foaf:name>Deputy</foaf:name>
</ex:Dog>

We have an OWL class ex:Dog (not shown), and one instance of that class: an individual whose URI is the XML base with deputy_dawg appended. So, if the xml:base is http://example.com/crimefighters#, the URI will be http://example.com/crimefighters#deputy_dawg. Now, suppose using Jena we have a reference to that resource. The frequently-asked question is: "how do I get the OWL classes for the resource?". Note that it really is OWL classes plural. We'll come back to this point in a bit. First let's do some setup.

An RDF resource is some thing, identified by zero or more URI's, about which we can make statements in a binary predicate form (e.g. "thing has-name Deputy""). In Jena, RDF resources are represented by the Resource class. The main RDF API, com.hp.hpl.jena.rdf.model provides classes and methods for manipulating resources and their properties. However, when working with OWL ontologies, there's a convenience API that extends the capabilities of the core RDF API. This is in com.hp.hpl.jena.ontology, and for convenience we'll call it the OntAPI. OntAPI has a Java class OntResource for representing general resources, and specialisations (Java sub-classes) of OntResource for resources that have special roles. So an resource denoting an OWL class has a convenience Java class OntClass, while a resource denoting an individual (such as our hapless canine lawman) is represented by the Java class Individual. So our question can be reformulated: get the OntClass resources denoting the OWL classes describing a given OntResource.

The primary method for doing this listRDFTypes. Why RDF types, not listOWLClasses()? The rationale is that the link from an individual to its OWL Class, RDFS class, etc, is via the RDF property rdf:type. However, this has proved confusing to some users, so I may add listOWLClasses() as an alias in a future release of Jena. Why listRDFTypes rather than getRDFType? In fact, getRDFType does exist, but isn't as useful as we might expect, for reasons discussed below. So let's see an example:

String NS = "http://example.com/crimefighters#"
OntModel m = ...   // assume this is the Jena model we're using

Individual dd = m.getIndividual( NS + "deputy_dawg" );

for (Iterator i = dd.listRDFTypes(false); i.hasNext(); ) {
    Resource cls = (Resource) i.next();
    System.out.println( "deputy_dawg has rdf:type " + cls );
}

This produces the output:

deputy_dawg has rdf:type http://example.com/crimefighters#Dog
deputy_dawg has rdf:type http://www.w3.org/2002/07/owl#Thing

Why does owl:Thing appear there? Simply put, it's an entailment added by the reasoner. All OWL classes are subclasses of owl:Thing. So any resource that has rdf:type T also has rdf:type owl:Thing by subsumption. In general, listRDFTypes will list all of the type statements in the model, no matter whether they were asserted or inferred. Unless the model was configured without a reasoner, there will normally be more than one rdf:type per resource, sometimes quite a lot. This is the reason why listRDFTypes is preferred: getRDFType will non-deterministically pick one of the available rdf:type's if there is more than one available. The caller has no control over which will be picked.

It's often convenient to have only the most specific type of an individual. This is what the Boolean direct flag denotes: if set to true, only the direct (most immediate) type statements are returned. We'll illustrate this by modifying the example slightly, starting with the ontology itself:

<owl:Class rdf:ID="Ally" />
<owl:Class rdf:ID="Dog">
  <rdfs:subClassOf>
    <owl:Class rdf:ID="FaithfulFriend" />
  </rdfs:subClassOf>
</owl:Class>

<ex:Dog rdf:ID="deputy_dawg">
  <foaf:name>Deputy</foaf:name>
  <rdf:type rdf:resource="#Ally" />
</ex:Dog>

Now the code:

for (Iterator i = dd.listRDFTypes( false ); i.hasNext(); ) {
  Resource cls = (Resource) i.next();
  System.out.println( "deputy_dawg has non-direct rdf:type " + cls );
}
for (Iterator i = dd.listRDFTypes( true ); i.hasNext(); ) {
  Resource cls = (Resource) i.next();
  System.out.println( "deputy_dawg has direct rdf:type " + cls );
}

Which produces the following output:

deputy_dawg has non-direct rdf:type http://example.com/crimefighters#Ally
deputy_dawg has non-direct rdf:type http://example.com/crimefighters#Dog
deputy_dawg has non-direct rdf:type http://www.w3.org/2002/07/owl#Thing
deputy_dawg has non-direct rdf:type http://example.com/crimefighters#FaithfulFriend

deputy_dawg has direct rdf:type http://example.com/crimefighters#Ally
deputy_dawg has direct rdf:type http://example.com/crimefighters#Dog

because only Ally and Dog are immediate (i.e. non-subsumed) types.

OntResource defines a number of convenience methods for manipulating the OWL class of a resource. Besides listing and getting the type (listRDFType and getRDFType as above), there are also methods for adding a new type (addRDFType), replacing all existing type assertions (note: assertions, not entailments) with a new type (setRDFType) and testing the type (hasRDFType). For full details, please see the Javadoc.

del.icio.us: jena, tutorial, semanticweb.

4 comments:

Anonymous said...

Thanks for the information! Was very helpful.

Anonymous said...

Thank you - that helped!

yihlamur said...

Is it possible to find inferred classes of an individual ?
Let's say that I have created a class called dangerousDog which is a subclassOf Dog.
Say that Dog class has a boolean property called isAggressive.
Whenever, the property isAgressive(true) and Dog, I want it to be classified in dangerousDog class.
Could you please show this in a code fragment using Jena like you did for non-inferred individuals above ?
Many Thanks

Gaurao Shegokar said...

Thank you so much...!