json-ld / json-ld.org

JSON for Linked Data's documentation and playground site
https://json-ld.org/
Other
856 stars 152 forks source link

How can I express owl:inverseOf in JSON-LD? #719

Open Radu3000 opened 4 years ago

Radu3000 commented 4 years ago

Let's say I have the famous Library.jsonld example:

{
  "@context": {
    "dc11": "http://purl.org/dc/elements/1.1/",
    "ex": "http://example.org/vocab#",
    "xsd": "http://www.w3.org/2001/XMLSchema#",
    "ex:contains": {
      "@type": "@id"
    }
  },
  "@graph": [
    {
      "@id": "http://example.org/library",
      "@type": "ex:Library",
      "ex:contains": "http://example.org/library/the-republic"
    },
    {
      "@id": "http://example.org/library/the-republic",
      "@type": "ex:Book",
      "dc11:creator": "Plato",
      "dc11:title": "The Republic",
      "ex:contains": "http://example.org/library/the-republic#introduction"
    },
    {
      "@id": "http://example.org/library/the-republic#introduction",
      "@type": "ex:Chapter",
      "dc11:description": "An introductory chapter on The Republic.",
      "dc11:title": "The Introduction"
    }
  ]
}

How can I say express that ex:contains is an inverse of ex:containedIn?

Thanks, Radu

gkellogg commented 4 years ago

owl:inverseOf can be used as with any other property in RDF, but it is used when defining a property to indicate it's semantic relationship to another property.

Within a document, you can achieve something similar without resorting to OWL reasoning using the @reverse keyword (see Reverse Properties).

In this case, you could define a "contains" term using @reverse like so:

{
  "@context": {
    "dc11": "http://purl.org/dc/elements/1.1/",
    "ex": "http://example.org/vocab#",
    "xsd": "http://www.w3.org/2001/XMLSchema#",
    "contains": {"@reverse": "ex:containedIn", "@type": "@id"}
  },
  "@graph": [
    {
      "@id": "http://example.org/library",
      "@type": "ex:Library",
      "contains": "http://example.org/library/the-republic"
    },
    {
      "@id": "http://example.org/library/the-republic",
      "@type": "ex:Book",
      "dc11:creator": "Plato",
      "dc11:title": "The Republic",
      "contains": "http://example.org/library/the-republic#introduction"
    },
    {
      "@id": "http://example.org/library/the-republic#introduction",
      "@type": "ex:Chapter",
      "dc11:description": "An introductory chapter on The Republic.",
      "dc11:title": "The Introduction"
    }
  ]
}
rylabo commented 2 years ago

Thought I'd weigh in, as I've wrestled with a similar issue.

If we treat JSON object keys (that are not reserved words for JSON-LD) as OWL properties, we can define a new term with a new JSON-LD object. Note however, that OWL makes a distinction between properties that can be assigned an Object (i.e. owl:ObjectProperty), and ones that can be assigned a Literal (i.e. owl:DatatypeProperty). It's the former you need in this case.

Assuming the ex:containedIn property already exists, you can include a node declaring a new property in OWL and include owl:inverseOf in a node, with a @type of owl:ObjectProperty as follows:

{
  "@context": {
    "dc11": "http://purl.org/dc/elements/1.1/",
    "ex": "http://example.org/vocab#",
    "ex:contains": { "@type": "@vocab" },
    "xsd": "http://www.w3.org/2001/XMLSchema#",
    "owl": "http://www.w3.org/2002/07/owl#",
    "owl:inverseOf" : {"@type": "@vocab"}  
    },
  "@graph": [
    {
      "@id": "http://example.org/vocab#contains",
      "@type": "owl:ObjectProperty",
      "owl:inverseOf": "ex:containedIn"
    },
    {
      "@id": "http://example.org/library",
      "@type": "ex:Library",
      "ex:contains": "http://example.org/library/the-republic"
    },
    {
      "@id": "http://example.org/library/the-republic",
      "@type": "ex:Book",
      "dc11:creator": "Plato",
      "dc11:title": "The Republic",
      "ex:contains": "http://example.org/library/the-republic#introduction"
    },
    {
      "@id": "http://example.org/library/the-republic#introduction",
      "@type": "ex:Chapter",
      "dc11:description": "An introductory chapter on The Republic.",
      "dc11:title": "The Introduction"
    }
  ]
}

Edit: added namespaces. Thanks @pchampin for the correction. Edit: corrections. Plural.

pchampin commented 2 years ago

Your example answers the original question, but in a very different way than @gkellogg's example above. More precisely, running your example through toRdf produces (among others), the following triples, which @gkellogg's example does not produce:

<http://example.org/library> ex:contains <http://example.org/library/the-republic>.
<http://example.org/library/the-republic> ex:contains <http://example.org/library/the-republic#introduction>.
ex:contains owl:inverseOf ex:containedIn.

On the other hand, the following triples are produced by @gkellogg's example, but not by yours:

<http://example.org/library/the-republic> ex:containedIn <http://example.org/library>.
<http://example.org/library/the-republic#introduction> ex:containedIn <http://example.org/library/the-republic>.

Mind you: from your example, one can infer the two triples above, but that requires extra machinery (namely, an OWL2 compliant inference engine) in addition to the JSON-LD processor. Depending on your use-case, that extra machinery might or might not be available, making one or the other approach preferable.

rylabo commented 2 years ago

In my particular use case, I wanted objects with references to BOTH ancestors and descendants, but @reverse discarded the inverse predicate. I also wasn't using a triple store, but MongoDB. I thought another may have had a similar use case, so I threw my example out there.

pchampin commented 2 years ago

In my particular use case, I wanted objects with references to BOTH ancestors and descendants, but @reverse discarded the inverse predicate.

Indeed. JSON-LD's expansion maps every JSON key to at most one RDF property.

I also wasn't using a triple store, but MongoDB. I thought another may have had a similar use case, so I threw my example out there.

Absolutely. I thought it was important to underline the differences between the two examples (for future readers), but as I wrote, both have their pros and cons.