Search code examples
javardfowlprotegeowl-api

How to update an OWL ontology file in order to add a new individual with some properties?


I have an ontology created in protege 4.3.0 and stored in an OWL file. I need to add some individuals to this ontology and update this file. Since these individuals are very similar to each other, I would like to take advantage of OWL API in order to add them faster than manual use of the program.

My aim is as follows:

  • add a new individual #individualSimpleSubType
  • add the type #SimpleSubType to this individual (#SimpleSubType is a subclass of #SimpleType)
  • add the following object property assertion to this individual: #hasProperty1 associated to #PropertyValue1
  • add the following object property assertion to this individual: #hasProperty2 associated to #PropertyValue2

The following information is already present in the ontology:

  • the class #SimpleType and its subclass #SimpleSubType
  • the object properties #hasProperty1 and #hasProperty2
  • the individuals #PropertyValue1 and #PropertyValue2

The following is the code I used to try to achieve that aim:

OWLOntologyManager manager = OWLManager.createOWLOntologyManager();

try {
    OWLOntology ontology = manager.loadOntologyFromOntologyDocument(new File("ontology.owl"));

    IRI ontologyIRI = ontology.getOntologyID().getOntologyIRI();
    PrefixManager prefixManager = new DefaultPrefixManager(ontologyIRI.toString().concat("#"));

    OWLDataFactory dataFactory = manager.getOWLDataFactory();

    OWLReasonerFactory reasonerFactory = new StructuralReasonerFactory();
    OWLReasoner reasoner = reasonerFactory.createReasoner(ontology);
    reasoner.precomputeInferences();

    // Get all subclasses of SimpleType stored within the loaded ontology.
    OWLClassNodeSet clsSimpleSubTypes = new OWLClassNodeSet();
    OWLClass simpleTypeClass = dataFactory.getOWLClass(":SimpleType", prefixManager);
    clsSimpleSubTypes.addDifferentEntities(reasoner.getSubClasses(simpleTypeClass, true).getFlattened());

    // Get two object properties stored within the loaded ontology.
    OWLObjectPropertyExpression objProperty1 = dataFactory.getOWLObjectProperty(IRI.create(ontologyIRI + "#hasProperty1"));
    OWLObjectPropertyExpression objProperty2 = dataFactory.getOWLObjectProperty(IRI.create(ontologyIRI + "#hasProperty2"));

    // Get two property values stored within the loaded ontology.
    OWLNamedIndividual propertyValue1 = dataFactory.getOWLNamedIndividual(IRI.create(ontologyIRI + "#PropertyValue1"));
    OWLNamedIndividual propertyValue2 = dataFactory.getOWLNamedIndividual(IRI.create(ontologyIRI + "#PropertyValue2"));

    for (OWLClass cls : clsSimpleSubTypes.getFlattened())
    {
        if (cls.getIRI().toString().endsWith("#SimpleSubType")) {
            // Create the new individual
            OWLNamedIndividual po = factory.getOWLNamedIndividual(IRI.create(ontologyIRI + "#individualSimpleSubType"));

            // individualSimpleSubType is of type SimpleSubType
            OWLClassAssertionAxiom assertion = dataFactory.getOWLClassAssertionAxiom(cls, po);
            manager.addAxiom(ontology, assertion);

            // individualSimpleSubType has object property PropertyValue1
            OWLObjectPropertyAssertionAxiom objProperty1Axiom = dataFactory.getOWLObjectPropertyAssertionAxiom(objProperty1, po, propertyValue1);
            manager.addAxiom(ontology, objProperty1Axiom);

            // individualSimpleSubType has object property PropertyValue2
            OWLObjectPropertyAssertionAxiom objProperty2Axiom = dataFactory.getOWLObjectPropertyAssertionAxiom(objProperty2, po, propertyValue2);
            manager.addAxiom(ontology, objProperty2Axiom);

            break;
        }
    }

    //File destinationFile = new File("ontology-new-data.owl");
    //OWLOntologyFormat format = manager.getOntologyFormat(ontology);
    //manager.saveOntology(ontology, format, IRI.create(destinationFile.toURI()));
    manager.saveOntology(ontology);
} catch (OWLOntologyCreationException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (OWLOntologyStorageException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

However, using the above code, I get a different result than the result I get using Protegé.

If I use Protegé in order to add a new individual as explained above, the OWL file is updated with the addition the following XML lines:

<!-- http://www.semanticweb.org/vincenzo/ontologies/2015/7/ontology.owl#individualSimpleSubType -->

<owl:NamedIndividual rdf:about="http://www.semanticweb.org/vincenzo/ontologies/2015/7/ontology.owl#individualSimpleSubType">
    <rdf:type rdf:resource="http://www.semanticweb.org/vincenzo/ontologies/2015/7/ontology.owl#SimpleSubType"/>
    <hasProperty1 rdf:resource="http://www.semanticweb.org/vincenzo/ontologies/2015/7/ontology.owl#PropertyValue1"/>
    <hasProperty2 rdf:resource="http://www.semanticweb.org/vincenzo/ontologies/2015/7/ontology.owl#PropertyValue2"/>
</owl:NamedIndividual>

However, if I use the above Java code, saving ontology also involves the modification of other lines: the same XML lines as above are added to the file, but also other lines are modified, as follows.

The folliwing lines are written at the beginning of the OWL file saved by the above Java code (but these lines are not written when I save the ontology using Protégé after performing the same modifies).

<!DOCTYPE rdf:RDF [
    <!ENTITY terms "http://purl.org/dc/terms/" >
    <!ENTITY owl "http://www.w3.org/2002/07/owl#" >
    <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" >
    <!ENTITY skos "http://www.w3.org/2004/02/skos/core#" >
    <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#" >
    <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
]>

Then, in the attributes of some tags are added certain prefixes, so:

  1. the attribute rdf:resource="&rdf;List" of the original OWL file is replaced by rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#List"
  2. the attribute rdf:resource="&owl;TransitiveProperty" of the original OWL file is replaced by rdf:resource="http://www.w3.org/2002/07/owl#TransitiveProperty"
  3. the attribute rdf:about="&terms;description" of the original OWL file is replaced by rdf:about="http://purl.org/dc/terms/description"
  4. the attribute rdf:resource="&rdfs;Literal" of the original OWL file is replaced by rdf:resource="http://www.w3.org/2000/01/rdf-schema#Literal"
  5. the attribute rdf:resource="&xsd;hexBinary" of the original OWL file is replaced by rdf:resource="http://www.w3.org/2001/XMLSchema#hexBinary"
  6. the attribute rdf:resource="&xsd;string" of the original OWL file is replaced by rdf:resource="http://www.w3.org/2001/XMLSchema#string"
  7. the attribute rdf:resource="&skos;Concept" of the original OWL file is replaced by rdf:resource="http://www.w3.org/2004/02/skos/core#Concept"
  8. the attribute rdf:resource="&skos;ConceptScheme" of the original OWL file is replaced by rdf:resource="http://www.w3.org/2004/02/skos/core#ConceptScheme"

What is the reason for this strange behavior?

In my Eclipse project I have imported the jar file related to OWL API 3.4.2, that is the same version integrated in Protegé 4.3.0.


Solution

  • The issue is with the prefixes in the original ontology not being preserved when reading it from the file. This has been fixed in more recent versions of OWL API. 3.5.2 and 4.0.2 (4.1.0 has not been released yet) should allow you to find the prefixes set in the ontology format object.

    In order to guarantee that these prefixes are used for entities as well (for things like &rdf; for example) you should call

    XMLWriterPreferences.getInstance().setUseNamespaceEntities(true);

    before saving the ontology.

    Note that these are not semantic differences - they are syntactic differences at the XML level only. The ontologies are semantically equivalent whether the prefixes are shortened to entities or not.