Kastor Gen Documentation

Welcome to the Kastor Gen documentation. Kastor Gen is the Kotlin code-generation layer for Kastor: it turns SHACL shapes and JSON-LD context into pure domain interfaces plus RDF-backed wrappers, wired together at runtime by OntoMapper.

Shared definitions (shape, literal, repository, materialization in the codegen sense vs reasoning): Glossary. How tutorials differ from reference across the site: How documentation fits together.

Navigate: tutorials explain workflows; reference/ is the API source of truth; Examples summarizes sample docs; for a runnable DCAT-style build, use the examples/dcat-us Gradle module on GitHub (see also Kastor examples).

Why Kastor Gen?

The Problem

Writing domain interfaces manually is:

  • ❌ Time-consuming: 1-2 hours per complex class
  • ❌ Error-prone: Typos, wrong IRIs, type mismatches
  • ❌ Hard to maintain: Must manually sync with ontology changes
  • ❌ Inconsistent: Code can drift from ontology definitions

The Solution

Kastor Gen generates type-safe interfaces automatically:

  • ✅ 90% faster: 2 minutes vs 1-2 hours per class
  • ✅ 100% consistent: Code always matches ontology
  • ✅ Type-safe: Compile-time validation from SHACL constraints
  • ✅ Easy updates: Regenerate when ontology changes

Key Benefits

Benefit Impact
90% less manual code Faster development
100% consistency Zero sync errors
Compile-time safety Fewer runtime bugs
Single source of truth Easier maintenance
Pure domain objects Clean business code
See detailed benefits → View comparisons →

Table of Contents

flowchart LR
  Ontology["SHACL + JSON-LD"] --> KSP["KSP processor"]
  KSP --> Dom["Domain interfaces"]
  KSP --> Wrap["Wrappers (lazy)\nOntoMapper.register"]
  KSP --> DC["Data classes (eager)\n+ Factory objects"]
  Graph["RdfGraph / RdfRepository"] --> Mat["materialize / asType"]
  Wrap --> Mat
  DC --> Mat
  Mat --> Dom

What is Kastor Gen?

Kastor Gen generates type-safe domain interfaces from your SHACL/JSON-LD ontologies, eliminating manual interface writing and ensuring your code always matches your data model.

Kastor Gen is a Kotlin library that provides:

🎯 Pure Domain Interfaces

Create clean, RDF-free domain interfaces that represent your business concepts:

// Using full IRIs
@Rdf(iri = "http://www.w3.org/ns/dcat#Catalog")
interface Catalog {
    @Rdf(iri = "http://purl.org/dc/terms/title")
    val title: String
    
    @Rdf(iri = "http://purl.org/dc/terms/description")
    val description: String
    
    @Rdf(iri = "http://www.w3.org/ns/dcat#dataset")
    val dataset: List<Dataset>
}

// Using QNames (recommended): `@file:Rdf(prefixes = …)` then `@Rdf(iri = "prefix:local")`
@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#"),
        Prefix("dcterms", "http://purl.org/dc/terms/")
    ]
)

package com.example

import com.geoknoesis.kastor.gen.annotations.Prefix
import com.geoknoesis.kastor.gen.annotations.Rdf

@Rdf(iri = "dcat:Catalog")
interface Catalog {
    @Rdf(iri = "dcterms:title")
    val title: String
    
    @Rdf(iri = "dcterms:description")
    val description: String
    
    @Rdf(iri = "dcat:dataset")
    val dataset: List<Dataset>
}

🔄 Automatic Materialization

Convert RDF nodes to domain objects seamlessly:

val catalog: Catalog = graph.materialize(iri("https://data.example.org/catalog"))

// Pure domain usage
println("Title: ${catalog.title}")
println("Dataset count: ${catalog.dataset.size}")

🚀 RDF Side-Channel Access

Access RDF power when needed without polluting domain interfaces:

// Side-channel access
val rdfHandle = catalog.asRdf()
val extras = rdfHandle.extras

// Access unmapped properties
val altLabels = extras.strings(SKOS.altLabel)
val allPredicates = extras.predicates()

// Validation
rdfHandle.validateOrThrow()

Ontology-driven code generation

Annotate a small generator class (or use @file:Rdf on the package file) with @Rdf(shacl = …, context = …). Paths are under src/main/resources. KSP emits the selected artifacts into the target package.

Kastor Gen supports two generation modes that can be used independently or together:

Mode Generated artifacts Loading Equality
Interface + Wrapper Domain interface + *Wrapper class Lazy (property delegates) Identity
Data class *Record data class + *RecordFactory object Eager (at factory time) Structural (data class)

Interface + Wrapper mode (default — live, lazy-loaded view):

@Rdf(
    shacl = "ontologies/dcat.shacl.ttl",
    context = "ontologies/dcat.context.jsonld",
    packageName = "com.example.generated",
    generateInterfaces = true,
    generateWrappers = true,
)
class OntologyGenerator

Data class mode (immutable snapshot — no RDF types in the class body):

@Rdf(
    shacl = "ontologies/dcat.shacl.ttl",
    context = "ontologies/dcat.context.jsonld",
    packageName = "com.example.generated",
    generateDataClass = true,
    dataClassSuffix = "Record",       // CatalogRecord, DatasetRecord, …
    nestedMode = NestedMode.DATA_CLASS, // recursive snapshots, or INTERFACE / IRI_ONLY
)
class OntologyGenerator

Both modes register in OntoMapper so graph.materialize<CatalogRecord>(node) works the same as for wrappers.

Key Features

✅ Type Safety

  • Compile-time validation of property types
  • Automatic mapping from SHACL datatypes to Kotlin types
  • Cardinality constraints enforced at the type level
  • Benefit: 100% type safety, zero runtime type errors

✅ Automatic Generation

  • Generate interfaces from SHACL shapes
  • Generate wrappers from JSON-LD context
  • Single source of truth (ontology files)
  • Benefit: 90% less manual code, 100% consistency

✅ Performance

  • Lazy evaluation of properties
  • Efficient RDF graph traversal
  • Minimal memory footprint
  • Benefit: No performance overhead vs manual code

✅ Flexibility

  • Pure domain interfaces with optional RDF access
  • Support for complex object relationships
  • Extensible validation system
  • Benefit: Clean business code, RDF power when needed

✅ Standards Compliance

  • Full SHACL (Shapes Constraint Language) support
  • JSON-LD context integration
  • RDF 1.1 specification compliance
  • Benefit: Industry-standard ontologies work out of the box

Quick Start

⚡ Quick Benefit: Instead of spending 1-2 hours writing a domain interface manually, Kastor Gen generates it in 2 minutes from your SHACL ontology. See how →

1. Add Dependencies

Kastor Gen is not yet published to Maven Central or the Gradle Plugin Portal. Requires JDK 21+.

Inside the Kastor build (or a composite build that includes it):

plugins {
    id("org.jetbrains.kotlin.jvm")
    id("com.google.devtools.ksp")
}

dependencies {
    implementation(project(":kastor-gen:runtime"))
    ksp(project(":kastor-gen:processor"))
}

Generated interfaces and data classes carry jakarta.validation annotations (@NotNull, @NotEmpty, @Size) by default; kastor-gen-runtime brings jakarta.validation-api onto your compile class path, so both mode examples above compile as they are. Set validationAnnotations = ValidationAnnotations.NONE to omit the annotations, or JAVAX and add javax.validation:validation-api (see the annotation reference).

From another project, first run ./gradlew :rdf:core:publishToMavenLocal :kastor-gen:runtime:publishToMavenLocal :kastor-gen:processor:publishToMavenLocal in a Kastor checkout, add mavenLocal() to your repositories, and depend on com.geoknoesis.kastor:kastor-gen-runtime / ksp("com.geoknoesis.kastor:kastor-gen-processor") with the Kastor build’s project version. See Getting Started.

Try the working examples in the repository:

./gradlew :examples:hello-codegen:run
./gradlew :examples:dcat-us:runGeneratedExample

2. Define Domain Interface

@Rdf(iri = "http://www.w3.org/ns/dcat#Catalog")
interface Catalog {
    @Rdf(iri = "http://purl.org/dc/terms/title")
    val title: String
    
    @Rdf(iri = "http://www.w3.org/ns/dcat#dataset")
    val dataset: List<Dataset>
}

3. Use Generated Wrappers

// KSP generates CatalogWrapper automatically
val catalog: Catalog = graph.materialize(iri("https://data.example.org/catalog"))

// Pure domain usage - no RDF dependencies in business code
println("Title: ${catalog.title}")
println("Datasets: ${catalog.dataset.size}")

🛡️ Benefit: Type-safe access with compile-time validation. The title property is guaranteed to exist (from SHACL minCount constraint).

4. Access RDF Side-Channel

// When you need RDF power
val rdfHandle = catalog.asRdf()
val extras = rdfHandle.extras

// Access unmapped properties
val altLabels = extras.strings(SKOS.altLabel)
val allPredicates = extras.predicates()

// Validate against SHACL
rdfHandle.validateOrThrow()

🔌 Benefit: Clean separation - pure domain interfaces for business logic, RDF side-channel when you need advanced features. Best of both worlds.

Architecture Overview

┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Domain        │    │   Kastor Gen     │    │   RDF Graph     │
│   Interfaces    │◄──►│   Runtime        │◄──►│   (Kastor)      │
│                 │    │                  │    │                 │
│ • Pure Kotlin   │    │ • Materialization│    │ • Triples       │
│ • No RDF deps   │    │ • Type mapping   │    │ • SPARQL        │
│ • Business logic│    │ • Validation     │    │ • Serialization │
└─────────────────┘    └──────────────────┘    └─────────────────┘
         │                       │                       │
         │                       │                       │
         ▼                       ▼                       ▼
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│   Generated     │    │   KSP Processor  │    │   SHACL/JSON-LD │
│   Wrappers      │◄───│                  │◄───│   Ontology      │
│                 │    │ • Code generation│    │   Files         │
│ • RDF-backed    │    │ • Type inference │    │                 │
│ • Lazy loading  │    │ • Validation     │    │ • Shapes        │
│ • Side-channel  │    │ • Registry       │    │ • Context       │
└─────────────────┘    └──────────────────┘    └─────────────────┘

Use Cases

📊 Data Catalogs

  • DCAT (Data Catalog Vocabulary) compliance
  • Government open data portals
  • Enterprise data governance

🏢 Enterprise Integration

  • RDF-based data lakes
  • Semantic web applications
  • Knowledge graphs

🔬 Research & Academia

  • Scientific data management
  • Research data repositories
  • Ontology-driven applications

🌐 Web Applications

  • Linked data publishing
  • Semantic search
  • Content management systems

Getting Help

📚 Documentation

💬 Community

  • GitHub Issues for bug reports
  • GitHub Discussions for questions
  • Stack Overflow with kastor-gen tag

🛠️ Examples

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development Setup

git clone https://github.com/geoknoesis/kastor.git
cd kastor
./gradlew build

Running Tests

./gradlew test

Building Documentation

./gradlew dokkaHtml

License

Kastor Gen is licensed under the Apache License 2.0.

Acknowledgments


Ready to get started?