RO-Crate Schema Plus

Same RO-Crate

+ Convention for interoperable schema and metadata.

+ Convention to include interoperable schema and metadata outside ro-crate-metadata.json.

Use Cases

  1. Exchanging metadata between systems using a common format (RO-Crate) but also a fully defined schema (Interoperability Profile)
  2. Validation of imported metadata thanks to the well defined schema (Interoperability Profile)
  3. Go beyond using the ro-crate-metadata.json to include metadata, to support large exports. (Extensibility Profile)

Profile Specifications

Profile Status Version Comments
Interoperability Profile Release 0.2
Extensibility Profile Draft 0.1

API Specifications

Profile Status Version Comments
Client API Release 0.2 Read/write RO-crates containing schema and metadata
Server API Draft 1.0 Exports/imports RO-crates containing schema and metadata

Libraries

Language Status Interoperability Profile Extensibility Profile Comments
Java Release Candidate 0.2 Reference implementation
Python Release Candidate 0.2 Python implementation with Pydantic support

Application Support

Application Status Interoperability Profile Extensibility Profile Comments
openBIS Active Development 0.2
SciCat Active Development 0.2
SciLog Active Development 0.2
AiiDAlab Planned

Quick Start Guide (Java)

In this example, we express two SQL tables in RO-Crate. It expresses experiments and each experiment can have a creator, who is a person.

SQL example source

CREATE TABLE person( -- https://schema.org/Person
    personid VARCHAR(255), 
    givenname VARCHAR(255), -- https://schema.org/givenName
    familyname VARCHAR(255), -- https://schema.org/familyName
    identifier VARCHAR (255), -- https://schema.org/identifier
    PRIMARY KEY(personid)


);

CREATE TABLE experiment(
    experimentid VARCHAR(255), 
    date DATETIME,
    name VARCHAR(255),
    creatorid VARCHAR(255), https://schema.org/creator
    PRIMARY_KEY(experimentid),
    FOREIGN KEY(creatorid) REFERENCES person(personid)
);

INSERT INTO PERSON (person_id, givenname, familyname, identifier) VALUES ('PERSON1', 'Meier', 'Andreas', 'https://orcid.org/0009-0002-6541-4637');
INSERT INTO PERSON (person_id, givenname, familyname, identifier) VALUES ('PERSON2', 'Fuentes', 'Juan', 'https://orcid.org/0009-0002-8209-1999');


INSERT INTO experiment (experimentid, date, name, creatorid) VALUES ('EXPERIMENT1', '2025-09-08 08:41:50', 'Example Experiment', 'Person1');



Tables are Types in our nomenclature, e.g.

IType personType = new Type()
Columns are PropertyTypes, e.g.
IPropertyType name = new PropertyType()
Both Types and PropertyTypes can be annotated to specify semantics. The rows are represented by IMetaDataEntry, their IType is specifed in the crate.

Write Ro-Crate Example

Java example source


import ch.eth.sis.rocrate.SchemaFacade;
import ch.eth.sis.rocrate.facade.*;
import edu.kit.datamanager.ro_crate.writer.FolderWriter;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class QuickStartWrite
{

    private static final String PREFIX = "Example";

    private static final String SEPARATOR = ":";

    public static final String TMP_EXAMPLE_CRATE = "/tmp/example-crate";

    public static void main(String[] args)
    {
        /* Setting up an RO-Crate with the schema facade */
        ISchemaFacade schemaFacade =
                new SchemaFacade("name", "description", "2024-12-04T07:53:11Z", "licenceIdentifier",
                        Map.of());

        Type personType = new Type();

        {
            personType.setId(PREFIX + SEPARATOR + "Person");
            personType.setOntologicalAnnotations(List.of("https://schema.org/Person"));

            {
                PropertyType personId = new PropertyType();
                personId.setId(PREFIX + SEPARATOR + "personid");
                personId.setTypes(List.of(LiteralType.STRING));
                personType.addProperty(personId);
            }
            {
                PropertyType givenName = new PropertyType();
                givenName.setId(PREFIX + SEPARATOR + "givenName");
                givenName.setOntologicalAnnotations(List.of("https://schema.org/givenName"));
                givenName.setTypes(List.of(LiteralType.STRING));
                personType.addProperty(givenName);
            }
            {
                PropertyType givenName = new PropertyType();
                givenName.setId(PREFIX + SEPARATOR + "familyName");
                givenName.setOntologicalAnnotations(List.of("https://schema.org/familyName"));
                givenName.setTypes(List.of(LiteralType.STRING));
                personType.addProperty(givenName);
            }
            {
                PropertyType identifier = new PropertyType();
                identifier.setId(PREFIX + SEPARATOR + "identifier");
                identifier.setOntologicalAnnotations(List.of("https://schema.org/identifier"));
                identifier.setTypes(List.of(LiteralType.STRING));
                personType.addProperty(identifier);
            }
            schemaFacade.addType(personType);

        }

        Type experimentType = new Type();

        /* Building our Experiment type */
        {
            experimentType.setId(PREFIX + SEPARATOR + "Experiment");

            {
                PropertyType experimentId = new PropertyType();
                experimentId.setId(PREFIX + SEPARATOR + "experimentid");
                experimentId.setTypes(List.of(LiteralType.STRING));
                experimentType.addProperty(experimentId);
            }
            {
                PropertyType creator = new PropertyType();
                creator.setId(PREFIX + SEPARATOR + "creator");
                creator.setOntologicalAnnotations(List.of("https://schema.org/creator"));
                creator.addType(personType);
                experimentType.addProperty(creator);
            }
            {
                PropertyType name = new PropertyType();
                name.setId(PREFIX + SEPARATOR + "name");
                name.setTypes(List.of(LiteralType.STRING));
                experimentType.addProperty(name);
            }
            {
                PropertyType date = new PropertyType();
                date.setId(PREFIX + SEPARATOR + "date");
                date.setTypes(List.of(LiteralType.DATETIME));
                experimentType.addProperty(date);
            }
            schemaFacade.addType(experimentType);

        }

        {
            MetadataEntry personAndreas = new MetadataEntry();
            personAndreas.setId("PERSON1");
            Map properties = new LinkedHashMap<>();
            personAndreas.setTypes(Set.of(personType.getId()));
            properties.put("givenname", "Andreas");
            properties.put("lastname", "Meier");
            properties.put("identifier", "https://orcid.org/0009-0002-6541-4637");
            personAndreas.setProps(properties);
            personAndreas.setReferences(new LinkedHashMap<>());
            schemaFacade.addEntry(personAndreas);

            MetadataEntry personJuan = new MetadataEntry();
            personJuan.setId("PERSON2");
            personJuan.setTypes(Set.of(personType.getId()));
            Map properties2 = new LinkedHashMap<>();
            properties2.put("givenname", "Andreas");
            properties2.put("lastname", "Meier");
            properties2.put("identifier", "https://orcid.org/0009-0002-6541-4637");
            personJuan.setProps(properties2);
            personJuan.setReferences(new LinkedHashMap<>());

            schemaFacade.addEntry(personJuan);

            MetadataEntry experiment1 = new MetadataEntry();
            experiment1.setId("EXPERIMENT1");
            experiment1.setReferences(Map.of("creator", List.of(personAndreas.getId())));
            experiment1.setTypes(Set.of(experimentType.getId()));
            Map propertiesExperiment = new LinkedHashMap<>();
            propertiesExperiment.put("name", "Example Experiment");
            propertiesExperiment.put("date", "2025-09-08 08:41:50.000");
            experiment1.setProps(propertiesExperiment);
            schemaFacade.addEntry(experiment1);

        }

        FolderWriter folderWriter = new FolderWriter();
        folderWriter.save(schemaFacade.getCrate(), TMP_EXAMPLE_CRATE);

    }

}


Read RO-Crate Example

Java example source

import ch.eth.sis.rocrate.SchemaFacade;
import ch.eth.sis.rocrate.facade.IMetadataEntry;
import ch.eth.sis.rocrate.facade.IPropertyType;
import ch.eth.sis.rocrate.facade.IType;
import com.fasterxml.jackson.core.JsonProcessingException;
import edu.kit.datamanager.ro_crate.RoCrate;
import edu.kit.datamanager.ro_crate.reader.FolderReader;
import edu.kit.datamanager.ro_crate.reader.RoCrateReader;

import java.util.List;

public class QuickStartRead
{

    public static void main(String[] args) throws JsonProcessingException
    {
        RoCrateReader reader = new RoCrateReader(new FolderReader());
        RoCrate crate = reader.readCrate(QuickStartWrite.TMP_EXAMPLE_CRATE);
        SchemaFacade schemaFacade = SchemaFacade.of(crate);

        List types = schemaFacade.getTypes();


        /* Writes out all types with their entries */
        for (IType type : types)
        {
            System.out.println(type);
            for (IMetadataEntry entry : schemaFacade.getEntries(type.getId()))
            {
                System.out.println(entry);
            }
        }
        /* Writes out all property types */
        for (IPropertyType propertyType : schemaFacade.getPropertyTypes())
        {
            System.out.println(propertyType);
        }

    }
}

Quick Start Guide (Python)

In this example, we express two SQL tables in RO-Crate using Python. It expresses experiments and each experiment can have a creator, who is a person.

SQL example source

CREATE TABLE person( -- https://schema.org/Person
    personid VARCHAR(255), 
    givenname VARCHAR(255), -- https://schema.org/givenName
    familyname VARCHAR(255), -- https://schema.org/familyName
    identifier VARCHAR (255), -- https://schema.org/identifier
    PRIMARY KEY(personid)


);

CREATE TABLE experiment(
    experimentid VARCHAR(255), 
    date DATETIME,
    name VARCHAR(255),
    creatorid VARCHAR(255), https://schema.org/creator
    PRIMARY_KEY(experimentid),
    FOREIGN KEY(creatorid) REFERENCES person(personid)
);

INSERT INTO PERSON (person_id, givenname, familyname, identifier) VALUES ('PERSON1', 'Meier', 'Andreas', 'https://orcid.org/0009-0002-6541-4637');
INSERT INTO PERSON (person_id, givenname, familyname, identifier) VALUES ('PERSON2', 'Fuentes', 'Juan', 'https://orcid.org/0009-0002-8209-1999');


INSERT INTO experiment (experimentid, date, name, creatorid) VALUES ('EXPERIMENT1', '2025-09-08 08:41:50', 'Example Experiment', 'Person1');



Tables are Types in our nomenclature, e.g.

personType = Type(id="id")
Columns are PropertyTypes, e.g.
name = TypeProperty(id="id")
Both Types and PropertyTypes can be annotated to specify semantics. The rows are represented by MetadataEntry, their Type is specified in the crate.

Write Ro-Crate Example

Python example source

from lib_ro_crate_schema.crate.schema_facade import SchemaFacade
from lib_ro_crate_schema.crate.type import Type
from lib_ro_crate_schema.crate.type_property import TypeProperty
from lib_ro_crate_schema.crate.metadata_entry import MetadataEntry
from lib_ro_crate_schema.crate.literal_type import LiteralType

TMP_EXAMPLE_CRATE = "output_crates/example-crate"

# Setting up an RO-Crate with the schema facade
schemaFacade = SchemaFacade()

personType = Type(id="id")

# Building Person type
personType.setId("Person")
personType.setOntologicalAnnotations(["https://schema.org/Person"])

personId = TypeProperty(id="id")
personId.setId("personid")
personId.setTypes([LiteralType.STRING])
personType.addProperty(personId)

givenName = TypeProperty(id="id")
givenName.setId("givenName")
givenName.setOntologicalAnnotations(["https://schema.org/givenName"])
givenName.setTypes([LiteralType.STRING])
personType.addProperty(givenName)

familyName = TypeProperty(id="id")
familyName.setId("familyName")
familyName.setOntologicalAnnotations(["https://schema.org/familyName"])
familyName.setTypes([LiteralType.STRING])
personType.addProperty(familyName)

identifier = TypeProperty(id="id")
identifier.setId("identifier")
identifier.setOntologicalAnnotations(["https://schema.org/identifier"])
identifier.setTypes([LiteralType.STRING])
personType.addProperty(identifier)

schemaFacade.addType(personType)

# Building Experiment type
experimentType = Type(id="id")
experimentType.setId("Experiment")

experimentId = TypeProperty(id="id")
experimentId.setId("experimentid")
experimentId.setTypes([LiteralType.STRING])
experimentType.addProperty(experimentId)

creator = TypeProperty(id="id")
creator.setId("creator")
creator.setOntologicalAnnotations(["https://schema.org/creator"])
creator.addType(personType)
experimentType.addProperty(creator)

name = TypeProperty(id="id")
name.setId("name")
name.setTypes([LiteralType.STRING])
experimentType.addProperty(name)

date = TypeProperty(id="id")
date.setId("date")
date.setTypes([LiteralType.DATETIME])
experimentType.addProperty(date)

schemaFacade.addType(experimentType)

# Creating metadata entries
personAndreas = MetadataEntry(id="id", class_id="id")
personAndreas.setId("PERSON1")
personAndreas.setClassId(personType.getId())
properties = {}
properties["givenname"] = "Andreas"
properties["lastname"] = "Meier"
properties["identifier"] = "https://orcid.org/0009-0002-6541-4637"
personAndreas.setProperties(properties)
personAndreas.setReferences({})
schemaFacade.addEntry(personAndreas)

personJuan = MetadataEntry(id="id", class_id="id")
personJuan.setId("PERSON2")
personJuan.setClassId(personType.getId())
properties2 = {}
properties2["givenname"] = "Juan"
properties2["lastname"] = "Meier"
properties2["identifier"] = "https://orcid.org/0009-0002-6541-4637"
personJuan.setProperties(properties2)
personJuan.setReferences({})
schemaFacade.addEntry(personJuan)

experiment1 = MetadataEntry(id="id", class_id="id")
experiment1.setId("EXPERIMENT1")
experiment1.setClassId(experimentType.getId())
experiment1.setReferences({"creator": [personAndreas.getId()]})
propertiesExperiment = {}
propertiesExperiment["name"] = "Example Experiment"
propertiesExperiment["date"] = "2025-09-08 08:41:50.000"
experiment1.setProperties(propertiesExperiment)
schemaFacade.addEntry(experiment1)

# Write to file
schemaFacade.write(TMP_EXAMPLE_CRATE, name="Python QuickStart Example")

Read RO-Crate Example

Python example source

from lib_ro_crate_schema.crate.schema_facade import SchemaFacade

TMP_EXAMPLE_CRATE = "output_crates/example-crate"

# Load RO-Crate from directory
schemaFacade = SchemaFacade.from_ro_crate(TMP_EXAMPLE_CRATE)

# Display types
types = schemaFacade.getTypes()

print("📚 Types in the crate:")
for typeObj in types:
    print(f"- Type {typeObj.getId()}: {typeObj.getComment() if typeObj.getComment() else ''}")
    entries = schemaFacade.getEntries(typeObj.getId())

    for entry in entries:
        print(f"{entry.getId()} ({entry.getClassId()}): {entry.properties}")

# Display property types
print("📚 Properties in the crate:")
properties = schemaFacade.getPropertyTypes()
for prop in properties:
    print(f"{prop.getId()}: {prop.getComment() if prop.getComment() else ''} Range: {prop.getRange()}")

Minimal Pydantic Example

Optionally you can create a RO-Crate using Pydantic models with the decorator-based approach. For a complete example with complex scientific workflows, object relationships, and round-trip import/export, see the full example.

Minimal Pydantic example source

from pydantic import BaseModel
from lib_ro_crate_schema.crate.decorators import ro_crate_schema, Field
from lib_ro_crate_schema.crate.schema_facade import SchemaFacade
from lib_ro_crate_schema.crate.jsonld_utils import add_schema_to_crate
from rocrate.rocrate import ROCrate
from pathlib import Path


# Define a simple Person schema
@ro_crate_schema(ontology="https://schema.org/Person")
class Person(BaseModel):
    """Person entity in the RO-Crate"""
    name: str = Field(json_schema_extra={"ontology": "https://schema.org/name"})
    email: str = Field(json_schema_extra={"ontology": "https://schema.org/email"})


# Create a Person instance
person = Person(name="Alice Researcher", email="alice@example.org")

# Build RO-Crate
facade = SchemaFacade()
facade.add_all_registered_models()
facade.add_model_instance(person, "alice")

# Create and export RO-Crate
crate = ROCrate()
crate.name = "Minimal Example"
crate.description = "A minimal RO-Crate with one Person"

final_crate = add_schema_to_crate(facade, crate)
output_path = Path("output_crates/minimal_example")
output_path.mkdir(parents=True, exist_ok=True)
final_crate.write(output_path)

print(f"✓ RO-Crate exported to: {output_path}")