Skip to content

Instantly share code, notes, and snippets.

@workergnome
Last active May 24, 2018 13:37
Show Gist options
  • Save workergnome/a161d3376a950ac7e283e6ec6630e1ec to your computer and use it in GitHub Desktop.
Save workergnome/a161d3376a950ac7e283e6ec6630e1ec to your computer and use it in GitHub Desktop.
Draft of the IIIF Extension documentation

Creating a IIIF Extension

Introduction

A IIIF extension is a mechanism to formally include additional properties within a IIIF resource. While the specification allows any properties to be added, most systems will ignore those properties, and there's no way to indicate to a client that a particular extension may have been used.

To solve this, we've specified a pattern to allow for the definition of reusable properties. the pattern includes:

  • A custom context defining the new properties in JSON-LD 1.1
  • Including that context within the manifest context block
  • A set of best practices for these extensions
  • A IIIF registry that allows discovery of community extensions.

A New Context

For each new IIIF Extension you MUST create a new, dereferencable context. This context MUST be a JSON-LD version 1.1 context, and thus MUST include "@version": 1.1. A proper IIIF Extension MUST define only one new property that is included in the global scope. Any other properties MUST be defined within a context nested under that property.

In practice, this means an extension may either define one new property whose value is a JSON literal or array of literals:

https://example.com/extension_context_1.json

{
    "@context": {
        "@version": 1.1,
        "newProperty1": "https://example.org#new-property-1"
    }
}

which would be used like so:

{
    @context: [
        "https://example.com/extension_context_1.json",
        "http://www.w3.org/ns/anno.jsonld",
        "http://iiif.io/api/presentation/3/context.json"
    ],
    {
        "id": "https://example.org/extension_context_1/manifest",
        "type": "Manifest",
        "newProperty1": "I'm an extension property"
    }
}

or an extension may define a property whose value is a JSON object or an array of JSON objects, and all other properties must be contained within that object or objects.

https://example.com/extension_context_2.json

{
    "@context": {
        "@version": 1.1,
        "newProperty2":
            "@id" : "https://example.org#new-property-2",
            "@context": {
                "example": "https://example.org#"
                "foo": "example:foo",
                "bar": "example:bar"
            }
        }
    }
}

which would be used like so:

{
    @context: [
        "https://example.com/extension_context_2.json",
        "http://www.w3.org/ns/anno.jsonld",
        "http://iiif.io/api/presentation/3/context.json"
    ],
    {
        "id": "https://example.org/extension_context_2/manifest",
        "type": "Manifest",
        "newProperty2": {
            "foo": "I'm a nested extension property",
            "bar": "I'm another nested property"
    }
}

Notice that in this context, we have used the full URI for the property, and only defined it as a prefix within the scoped context. This keeps us from defining both example and newProperty2 within the global JSON-LD scope.

You should explicitly indicate the expected cardnality of the extension: if it would be valid for there to be more than one instance of your extension on a given IIIF Resource, you MUST indicate that by declaring this by including "@container": "@set" or "@container": "@list" on your included property. If there can only be one instance per IIIF Resource, you MUST NOT set the @container property to @set or @list.

(Note: This functionaliy could also be implemented using Type nesting, instead of Property nesting. However, the JSON-LD spec only allows defining one scoped context per Type; aadditional scoped contexts overwrite the previous value, which makes Type nesting prone to unintentional conflicts based on the order of the included @context values. So don't do that.)

Adding your Extension Context to a IIIF Resource

As represented in the manifest examples above, every IIIF resource that uses the extension MUST include the URI of the new context within the @context array of that resource. It MUST occur before both "http://www.w3.org/ns/anno.jsonld" and "http://iiif.io/api/presentation/3/context.json", so that extensions cannot accidentally overwrite the canonical IIIF contexts.

Best Practices

Self-documentation

Often, a IIIF resource will be discovered by an external user who is unaware of how or what the purpose and functionality of this extension might be. A sophisticated user might know how to interpret the RDF values of the context, but many extensions will not define an explicit ontology, and many users will not not how to find or interpret that ontology. To assist, we have three properties where explicit self-documentation can be included: type, label, and profile, described below.

All Top-level Resources should have a Type

Almost all extensions will involve creating a new Resource Type, which means that it will be important that at least the root object included via your extension property SHOULD include a "type" property. This MUST be a URL, and SHOULD be prefixed into a plain string within your extension. They MUST NOT have more than one type. Additional objects MAY have explicit types, or those types may be implicit and inferred from context.

All Top-level Resources should be labeled

Each extension SHOULD include a label property on the root object included via the extension property, describing for a user the content of the extension. If included, this MUST use the IIIF languageMap format: "label: {"en":["example label"]}.

All Top-level Resources may indicate a Profile

Each extension MAY include a profile property, containing a single URL that dereferences to a human-readable description of the extension and how it extends the IIIF Resource. It MUST not be compacted or curried with a namespace--it should be a full URL. This is useful for documentation and can also be used to differentiate between different implementations of the extension.

External Documentation

Each extension SHOULD have publicly-available documentation. This documentation SHOULD include:

  • a description of the properties in use
  • a list of the IIIF resource types for which this extension would be valid
  • an example of the syntax of the extension
  • a link to a IIIF Resource that demonstrates the extension in use

It is also useful to also include examples of both publishers and consumers of this extension, if any exist.

Using RDF

RDF best practices are that any property used within an RDF graph SHOULD be dereferencable. Behind the scenes, JSON-LD documents are a serialization of RDF (and the properties included in IIIF Resources are dereferencable), and so the classes and properties used in extensions SHOULD be dereferencable. They MAY resolve into a RDF or OWL ontology, or they MAY only dereference using fragment identifiers to an HTML page containing human-readable descriptions.

If a formal ontology is used, you MAY use rdfs:domain on the extension property to indicate which IIIF resources are targeted by the extension. Be aware that almost no systems enforce these constraints, so treat them as informative, not normative.

If you are defining new properties but not using an explicitly defererencable ontology, you SHOULD use the URL of the documentation page as the prefix for new properties within the context, and you SHOULD use the Cool URL Hash Syntax to assign URLs to those properties. Properties included from explicit ontologies should use the appropriate URLs as defined in those ontologies. When writing your documentation, you SHOULD use named anchors to link the URIs of the properties within the context to the description of those properties within the documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment