Skip to content

Content Wrappers

Whenever possible, the same data structures are used to represent the same types of data, regardless of the API operation (reading, creating, or modifying). However, often more data is available in output than in input. For example, when a value is read from the triplestore, its IRI is available, but when it is being created, it does not yet have an IRI.

The implementation of API v2 therefore uses content wrappers. For each type, there is a case class that represents the lowest common denominator of the type, the data that will be present regardless of the API operation. For example, the trait ValueContentV2 represents a Knora value, regardless of whether it is received as input or returned as output. Case classes such as DateValueContentV2 and TextValueContentV2 implement this trait.

An instance of this lowest-common-denominator class, or "content class", can then be wrapped in an instance of an operation-specific class that carries additional data. For example, when a Knora value is returned from the triplestore, a ValueContentV2 is wrapped in a ReadValueV2, which additionally contains the value's IRI. When a value is created, it is wrapped in a CreateValueV2, which has the resource IRI and the property IRI, but not the value IRI.

A read wrapper can be wrapped in another read wrapper; for example, a ReadResourceV2 contains ReadValueV2 objects.

In general, DSP-API v2 responders deal only with the internal schema. (The exception is OntologyResponderV2, which can return ontology information that exists only in an external schema.) Therefore, a content class needs to be able to convert itself from the internal schema to an external schema (when it is being used for output) and vice versa (when it is being used for input). Each content class class should therefore extend KnoraContentV2, and thus have a toOntologySchema method or converting itself between internal and external schemas, in either direction:

/**
  * A trait for content classes that can convert themselves between internal and internal schemas.
  *
  * @tparam C the type of the content class that extends this trait.
  */
trait KnoraContentV2[C <: KnoraContentV2[C]] {
    this: C =>
    def toOntologySchema(targetSchema: OntologySchema): C
}

Since read wrappers are used only for output, they need to be able convert themselves only from the internal schema to an external schema. Each read wrapper class should extend KnoraReadV2, and thus have a method for doing this:

/**
  * A trait for read wrappers that can convert themselves to external schemas.
  *
  * @tparam C the type of the read wrapper that extends this trait.
  */
trait KnoraReadV2[C <: KnoraReadV2[C]] {
    this: C =>
    def toOntologySchema(targetSchema: ApiV2Schema): C
}

Last update: January 19, 2023