From Agentgroup
Jump to: navigation, search

We wanted to provide a user, who is more familiar with object-oriented model then RDF graphs with a tool, which still allows her to inject RDF-demarcated data into the Ecosystem and use features of RDF language such as shared properties. The Java2RDF tool translates run-time state of the Java object (property values) and its schema (definition of it's Java class) into RDF graph.

This transformation does not preserve explicit information what Java object/clkass does the RDF resource come form and therefore, the tool cannot be used as to reverse the process and instanciate. In this terms it is different from persistency frameworks like Jenabean (persisting Java objects in form of RDF graphs) or Hibernate (persisting Java objects in relational database). However, both frameworks has inspired our solution.


Why not re-use something existing ?

There are a few tools for persisting Java objects in RDF graphs. The most popular (according to Google search ranking) is Jenabean. We haven't decided to modify it, because:

  • we do not need its whole functionality (we need one-way transformation, not persistency layer)
  • we want schema (Java class) and values (Java objects) to be translated into two separate RDF graphs, because there are different requirements for storing them: schema is more static (changes rarely) and should be uploaded to the repositorty, with ACID warranty, whereas data changes more often and can be dirty.
  • JenaBean access object's fields value through a set of getter methods (and thus why it is called after Java Bean idea), whether we personally prefer more straightforward way through publicly declared fields of an object.

JenaBean also suffers from:

  • not preserving sub-class relations,
  • language incosistency (classes are translated into OWL (as instances of owl:Class) and properties into pure RDFS (as instances of rdf:Property, not as owl:ObjectProperty or owl:DatatypeProperty).

TODO: Find a paper, when Java 2 RDF mapping tools were compared -- current state of the art

How it works

Instance identification

URI for an object of a class is generated in the following way:

  • A public method returning a String value, annotated with @Id annotation is used to obtain fulll URI of an object. For example:
 public static final String BASE = "http://ecosystem.it/core#";
 @Id
 public String getId() {
   return BASE + name;
 }
  • If no such method exists, the following URI is generated: http://package.name#hashCode where hashCode is a value returned by a hashcode() method of an object.

Serialized types

The Java2RDF#writeInstanceValues() method supports serializing the following types:

  • regular classes,
  • primitives (but only as values of an object's fields),
  • arrays.

Arrays serialization

An array is serialiazed in the following way:

  • to anynomyous RDF nodes of rdf:Seq type, when they are values of field of serialiazed class (NOTE: instead we're thinking about serializing into separate statements, with an instance of a class mapped to the statement's subject, a field name mapped to the statement's property, an array element as an statement's object)
  • each element of an array is serialiazed seperately, when if the array is passed directly as an argument to Java2RDF#writeInstanceValues() method.

Primitives serialization

Primitives are serialiazed to typed literals according to the following table:

Java class/primitive type XSD Datatype
Float/float xsd:float
Double/double xsd:double
Integer/int xsd:int
Long/long xsd:long
Short/short xsd:short
Byte/byte xsd:byte
BigDecimal xsd:decimal
BigInteger xsd:integer
Boolean/boolean xsd:boolean
String xsd:string

Class serialization

All public non-static fields of a class are serialized recursively as RDF properties. Inheritance relation between classes is serialized to rdfs:subClassOf predicate. Example: TODO:

 package some.package;
 @Entity
 public class LocableObject {
 }
 @Entity
 @URI ("http://domain.com/schema#Sensor ")
 public class Sensor extend LocableObject {
   Observation[] madeObservation;
   @URI("http://domain.com/schema#name")
   String name ;
   @Id
   public void getId() {
     return "http://domain.com/data#" + name ;
   }
 }

The schema will be:

 @prefix schema: <http://domain.com/schema#> .
 @prefix data: <http://domain.com/data#> .
 @prefix pck: <http://some.package#> .
 schema:Sensor a rdfs:Class ;
               rdfs:subClassOf pck:LocableObject .
 schema:name a rdf:Property .
 # No explicit URI given, using default behaviour for generating URI
 schema:Sensor_madeObservation a rdf:Property .

Static type vs. runtime type

Maciej->Raffaele: Can you describe what is the difference between this types and how Java2RDF handle with mapping a schema here ?

List of modifications to JenaBean

Items are ordered with decreasing priority.

  1. Assert subclassing relationships among Java classes with the rdf:subClassOf property (Raffaele)
  2. Introduce @Id and @RDF annotations (Maciej):
    1. @Id -- Marks the method retuning unique identifier of the instance. This is optional, by default hashcode() method is used to generate pseudo-unique (!) ids. Question: Is returnig value localname or full URI ?? Better full URI, like in this example:
 public static final String BASE = "http://ecosystem.it/core#";
 @Id
 public String getId() {
   return BASE + name;
 }
    1. @URI -- Proposition (taken from Sommer framework, where it is call @RDF) to use this instead of @Namespace and @RDFProperty annotations (from JenaBean framework) to depict the RDF Resouce for a particul field/property, class in JavaBean
  1. Just use the deep Java object exploration approach (like in java Serialization) and introduce the transient keyword and the @transient annotation to prevent a field from being asserted as an rdf property. Null fields are not asserted by default. (Raffaele)
  2. Add two separate methods for sending only the Schema (e.g. dc:Article a owl:class, dc:Article rdf:subClassOf dc:Book) and only the instance values of asserted fields. (both)

Cases

Domain and range mapping

 @URI("A")
 public class A {
   @URI("x")
   T1 field1;
 }
 @URI("B")
 public class B {
   @URI("x")
   T2 field2;
 }

If we try to map fields of both classes straigth-forward to RDF properties, we would receive:

 x a rdf:Property ;
   rdfs:domain A, B ;
   rdfs:range T1, T2 .

Such a usage of rdfs:domain property would mean that the subjects of statements with property x would be seen as instances of both stated classes: A, B. This is because RDFS documentation says:

  • Where a property P has more than one rdfs:domain property, then the resources denoted by subjects of triples with predicate P are instances of all the classes stated by the rdfs:domain properties.

Similary for the rdfs:range property, the objects of statements with property x would be seen as instances of both stated types (classes): T1, T2. This is because RDFS documentation says:

  • Where P has more than one rdfs:range property, then the resources denoted by the objects of triples with predicate P are instances of all the classes stated by the rdfs:range properties.

This could be solved in OWL by using owl:UnionOf class, but RDFS does not provide such a feature. Of course this can be solved manually, by creating such a thing. If your property wants to have range unionOf(T1,T2) then you could create an rdfs:Class U, make T1 and T2 subClasses of U and make U the range on your property. This captures about as much about this situation as you can in RDFS. The same relates to the rdfs:domain property.

But we do not handle with ranges and domains of properties now.

Internal static class

For the following classes ThingWithoutIdAnnotation and WrappingThing.ThingWithoutIdAnnotation living in the same package jenaobject.test and defined as follows:

 public class ThingWithoutIdAnnotation {
 }
 public class WrappingThing {
   public static class ThingWithoutIdAnnotation {
   }
 }

will be mapped by Java2RDF into the same resource with the following URI: http://jenaobject.test#ThingWithoutIdAnnotation. This should be solved somehow...