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.