Frequently Asked Questions (FAQ)
This FAQ addresses common questions about Kastor Gen.
Table of Contents
- General Questions
- Domain Interface Design
- RDF Integration
- Ontology Generation
- Performance
- Troubleshooting
- Migration
General Questions
What is Kastor Gen?
Kastor Gen is a Kotlin library that bridges the gap between RDF ontologies and domain objects. It provides:
- Pure domain interfaces with no RDF dependencies
- Automatic materialization from RDF to domain objects
- RDF side-channel access for advanced use cases
- Ontology-driven code generation from SHACL and JSON-LD
How is Kastor Gen different from other RDF libraries?
Unlike traditional RDF libraries that expose RDF types directly, Kastor Gen provides:
- Pure domain objects - Your business code doesn’t know about RDF
- Type safety - Compile-time validation of property types
- Lazy evaluation - Properties are loaded only when accessed
- Side-channel access - RDF power when you need it
What RDF backends does Kastor Gen support?
Kastor Gen supports multiple RDF backends through Kastor:
- Apache Jena - Full-featured RDF framework
- RDF4J - Eclipse RDF4J framework
- SPARQL - Remote SPARQL endpoints
- Memory - In-memory RDF storage
Domain Interface Design
How do I design domain interfaces?
Follow these principles:
- Keep interfaces pure - No RDF types in domain interfaces
- Use business-focused names - Choose names that reflect business concepts
- Handle cardinality appropriately - Use
List<T>for multiple values - Make properties immutable - Use
valinstead ofvar
@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>
}
Can I use inheritance in domain interfaces?
Yes, but keep it simple:
// ✅ Good: Simple inheritance
interface Resource {
val iri: String
}
interface Catalog : Resource {
val title: String
}
// ❌ Avoid: Complex inheritance
interface ComplexCatalog : Resource, Temporal, Spatial {
// Can cause issues with property resolution
}
How do I handle optional properties?
Use nullable types for optional properties:
interface Person {
@Rdf(iri = "http://xmlns.com/foaf/0.1/name")
val name: String // Required
@Rdf(iri = "http://xmlns.com/foaf/0.1/age")
val age: Int? // Optional
}
Can I use custom types in domain interfaces?
Yes. Register a factory for the type with OntoMapper.register; materialize then uses it:
data class CustomDate(val year: Int, val month: Int, val day: Int)
OntoMapper.register(CustomDate::class.java) { handle ->
val lexical = KastorGraphOps.getLiteralValues(handle.graph, handle.node, Iri("http://example.org/date"))
.firstOrNull()?.lexical ?: error("missing date")
val (y, m, d) = lexical.split("-").map(String::toInt)
CustomDate(y, m, d)
}
For literal values inside generated or hand-written code, the XsdLiterals decoders
(localDate, bigDecimal, …) are usually enough.
RDF Integration
How do I access RDF side-channel?
Use the asRdf() extension function:
val catalog: Catalog = catalogRef.asType()
val rdfHandle = catalog.asRdf()
// Access unmapped properties
val extras = rdfHandle.extras
val altLabels = extras.strings(SKOS.altLabel)
// Validate against SHACL
rdfHandle.validateOrThrow()
How do I handle validation?
OntoMapper supports SHACL validation:
// Validate during materialization
val validation = JenaValidation.fromTurtle(shapesTtl) // shapesTtl: your SHACL shapes as Turtle text
val catalog: Catalog = catalogRef.asValidatedType(validation)
// Or validate explicitly
val rdfHandle = catalog.asRdf()
try {
rdfHandle.validateOrThrow()
} catch (e: ValidationException) {
// Handle validation error
}
How do I access unmapped properties?
Use the extras property bag:
val rdfHandle = catalog.asRdf()
val extras = rdfHandle.extras
// Get all predicates
val predicates = extras.predicates()
// Get values for specific predicate
val values = extras.values(SKOS.altLabel)
val strings = extras.strings(SKOS.altLabel)
val iris = extras.iris(SKOS.altLabel)
// Materialize objects
val objects = extras.objects(SKOS.altLabel, SomeType::class.java)
How do I handle different RDF backends?
Choose the appropriate backend for your needs:
// Memory backend (development)
val repo = Rdf.memory()
// Jena backend (production)
val repo = Rdf.repository {
providerId = "jena"
variantId = "memory"
}
// RDF4J backend (production)
val repo = Rdf.repository {
providerId = "rdf4j"
variantId = "memory"
}
// SPARQL backend (remote)
val repo = RdfProviderRegistry.create(
RdfConfig.of(
providerId = ProviderId("sparql"),
variantId = VariantId("sparql"),
options = mapOf("location" to "http://example.org/sparql")
)
)
Ontology Generation
How do I generate code from SHACL and JSON-LD?
Use @Rdf(shacl = …, context = …) on a generator class (or @file:Rdf):
import com.geoknoesis.kastor.gen.annotations.Rdf
@Rdf(
shacl = "ontologies/dcat.shacl.ttl",
context = "ontologies/dcat.context.jsonld",
packageName = "com.example.generated",
)
class OntologyGenerator
What SHACL features are supported?
Currently supported:
- NodeShapes with
sh:targetClass - Property constraints with
sh:path - Datatype constraints with
sh:datatype - Cardinality constraints with
sh:minCount/sh:maxCount - Class constraints with
sh:class
Not yet supported:
- Complex constraint combinations (
sh:or,sh:and) - Custom validation rules
- Shape inheritance
How do I handle custom datatypes?
Map custom datatypes to Kotlin types:
// In SHACL
sh:property [
sh:path custom:customDate ;
sh:datatype custom:CustomDate ;
] .
// In JSON-LD context
"customDate": {
"@id": "http://example.org/customDate",
"@type": "custom:CustomDate"
}
Can I generate code from multiple ontologies?
Yes. Use one generator class (or @file:Rdf) per ontology bundle:
import com.geoknoesis.kastor.gen.annotations.Rdf
@Rdf(
shacl = "ontologies/dcat.shacl.ttl",
context = "ontologies/dcat.context.jsonld",
packageName = "com.example.dcatus.generated",
)
class DcatGenerator
@Rdf(
shacl = "ontologies/foaf.shacl.ttl",
context = "ontologies/foaf.context.jsonld",
packageName = "com.example.foaf.generated",
)
class FoafGenerator
Performance
How does OntoMapper handle performance?
OntoMapper is optimized for performance:
- Lazy evaluation - Properties are loaded only when accessed
- Efficient graph traversal - Uses optimized RDF graph operations
- Minimal memory footprint - Only loads what you need
- Caching - Results are cached for repeated access
How do I optimize for large datasets?
For large datasets:
- Use side-channel access for bulk operations
- Minimize property access - Only access what you need
- Use appropriate backends - Choose backend based on data size
- Implement pagination - For large result sets
// ✅ Good: Bulk access
val rdfHandle = catalog.asRdf()
val extras = rdfHandle.extras
val allPredicates = extras.predicates()
// ❌ Avoid: Individual property access
val catalog: Catalog = catalogRef.asType()
val title = catalog.title
val description = catalog.description
How do I handle memory usage?
To minimize memory usage:
- Use lazy evaluation - Properties are loaded on demand
- Close RDF repositories - When done with them
- Use appropriate backends - Memory backend for small datasets
- Implement streaming - For very large datasets
// ✅ Good: Proper resource management
val repo = Rdf.memory()
try {
val catalog: Catalog = catalogRef.asType()
// Use catalog
} finally {
repo.close()
}
Troubleshooting
Why is my generated code not compiling?
Common issues:
- Missing dependencies - Ensure KSP processor is configured
- Annotation processing - Check KSP configuration
- Package conflicts - Ensure unique package names
- Type mismatches - Check SHACL datatype mappings
Why are my properties not loading?
Check these:
- Property annotations — Ensure
@Rdf(iri = …)is present on the property or its getter/setter, and that the import iscom.geoknoesis.kastor.gen.annotations.Rdf. - IRI matching - Verify IRI matches RDF data
- Type mapping - Check datatype conversion
- Graph content - Ensure RDF data exists
How do I debug materialization issues?
Use debugging techniques:
// Enable debug logging
val repo = Rdf.memory()
repo.add { /* your RDF data */ }
// Check graph content
val triples = repo.defaultGraph.getTriples()
println("Graph has ${triples.size} triples")
// Check specific node
val node = iri("http://example.org/catalog")
val nodeTriples = triples.filter { it.subject == node }
println("Node has ${nodeTriples.size} triples")
// Try materialization
val catalog: Catalog = catalogRef.asType()
Why is validation failing?
Common validation issues:
- Missing required properties - Check SHACL constraints
- Wrong datatypes - Verify property types
- Cardinality violations - Check min/max counts
- Shape mismatches - Ensure correct target classes
How do I handle circular references?
Circular references are handled automatically:
interface Person {
@Rdf(iri = "http://xmlns.com/foaf/0.1/knows")
val knows: List<Person> // Circular reference
}
// OntoMapper handles this automatically
val person: Person = personRef.asType()
val friends = person.knows
val friendsOfFriends = friends.flatMap { it.knows }
Migration
How do I migrate from other RDF libraries?
Migration steps:
- Define domain interfaces - Create pure Kotlin interfaces
- Map RDF properties - Add
@Rdfannotations - Replace RDF access - Use OntoMapper materialization
- Update business logic - Work with domain objects
- Add side-channel access - For advanced RDF operations
How do I migrate from manual wrappers?
If you have manual wrapper classes:
- Extract interfaces - Create domain interfaces
- Replace manual code - Use generated wrappers
- Update registrations - Use
OntoMapper.register(type) { handle -> ... } - Test thoroughly - Ensure behavior is preserved
How do I upgrade OntoMapper versions?
Upgrade process:
- Check changelog - Review breaking changes
- Update dependencies - Use latest versions
- Regenerate code - Generated wrappers and factories call runtime APIs (
OntoMapper.register,XsdLiterals); code generated by an earlier version must be regenerated (clean build or re-run the generation task), not reused - Run tests - Ensure compatibility
- Update code - Apply any required changes
- Validate - Test with your data
Getting Help
Where can I get help?
- Documentation - Check the documentation
- Examples - See sample applications
- GitHub Issues - Report bugs and request features
- GitHub Discussions - Ask questions and share ideas
- Stack Overflow - Use the
kastor-gentag
How do I report bugs?
When reporting bugs, include:
- OntoMapper version - Version you’re using
- Kotlin version - Kotlin version
- RDF backend - Which backend you’re using
- Reproduction steps - How to reproduce the issue
- Expected behavior - What you expected to happen
- Actual behavior - What actually happened
- Sample code - Minimal code to reproduce
- Sample data - RDF data that causes the issue
How do I request features?
Feature requests should include:
- Use case - Why you need this feature
- Proposed solution - How it should work
- Alternatives - Other ways to achieve the goal
- Impact - Who would benefit from this feature
- Implementation ideas - How it might be implemented
Still have questions? Check out our documentation or ask for help!