Prefix Mappings and QNames

This guide explains how to use prefix mappings and QNames (qualified names) in Kastor Gen annotations to make your code more readable and maintainable.

Table of Contents

Overview

Instead of using full IRIs in your annotations:

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

You can use QNames when prefixes are in scope. Prefer @file:Rdf(prefixes = …) at the top of the Kotlin file (applies to every @Rdf(iri = …) in that file), or pass prefixes = […] on the domain @Rdf for type-local bindings:

@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
}

Basic Usage

Declaring prefix mappings

Declare prefixes with @file:Rdf(prefixes = …) (recommended for a whole file) or with prefixes = […] on a domain @Rdf alongside iri:

@Rdf(
    iri = "dcat:Catalog",
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#"),
        Prefix("dcterms", "http://purl.org/dc/terms/"),
        Prefix("foaf", "http://xmlns.com/foaf/0.1/")
    ],
)
interface Catalog { /* … */ }

Using QNames in Annotations

Once declared, you can use QNames in @Rdf(iri = …) on types and properties:

@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>
}

File-Level Prefix Mappings

You can declare prefix mappings at the file level for all classes in the file:

@file:Rdf(
    prefixes = [
        Prefix("vcard", "http://www.w3.org/2006/vcard/ns#"),
        Prefix("org", "http://www.w3.org/ns/org#")
    ]
)

package com.example

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

@Rdf(iri = "vcard:Individual")
interface Person {
    @Rdf(iri = "vcard:fn")
    val fullName: String
}

@Rdf(iri = "org:Organization")
interface Organization {
    @Rdf(iri = "vcard:fn")
    val name: String
}

Advanced Features

Mixed Usage

You can mix QNames and full IRIs in the same interface:

@Rdf(
    iri = "http://www.w3.org/ns/dcat#Catalog",
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#"),
        Prefix("dcterms", "http://purl.org/dc/terms/")
    ],
)
interface Catalog {
    @Rdf(iri = "dcterms:title") // QName
    val title: String
    
    @Rdf(iri = "http://purl.org/dc/terms/description") // Full IRI
    val description: String
    
    @Rdf(iri = "dcat:dataset") // QName
    val dataset: List<Dataset>
}

Custom Prefixes

Define your own prefixes for custom vocabularies:

@Rdf(
    iri = "ex:Location",
    prefixes = [
        Prefix("ex", "http://example.org/vocab#"),
        Prefix("schema", "http://schema.org/"),
        Prefix("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#")
    ],
)
interface Location {
    @Rdf(iri = "ex:name")
    val name: String
    
    @Rdf(iri = "schema:address")
    val address: String
    
    @Rdf(iri = "geo:lat")
    val latitude: Double
    
    @Rdf(iri = "geo:long")
    val longitude: Double
}

Multiple Prefix Mappings

You can declare multiple prefix mappings for different contexts:

// For DCAT vocabulary
@Rdf(
    iri = "dcat:Catalog",
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#"),
        Prefix("dcterms", "http://purl.org/dc/terms/")
    ],
)
interface Catalog { /* ... */ }

// For FOAF vocabulary
@Rdf(
    iri = "foaf:Person",
    prefixes = [
        Prefix("foaf", "http://xmlns.com/foaf/0.1/")
    ],
)
interface Person { /* ... */ }

Best Practices

1. Use Standard Prefixes

Use well-known prefixes for common vocabularies:

// ✅ Good: Standard prefixes
@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#"),
        Prefix("dcterms", "http://purl.org/dc/terms/"),
        Prefix("foaf", "http://xmlns.com/foaf/0.1/"),
        Prefix("skos", "http://www.w3.org/2004/02/skos/core#"),
        Prefix("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"),
        Prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#")
    ]
)

// ❌ Avoid: Non-standard prefixes
@file:Rdf(
    prefixes = [
        Prefix("dc", "http://www.w3.org/ns/dcat#"), // Should be "dcat"
        Prefix("dublin", "http://purl.org/dc/terms/") // Should be "dcterms"
    ]
)

Group related prefixes together:

// ✅ Good: Grouped by domain
@file:Rdf(
    prefixes = [
        // Data catalog vocabulary
        Prefix("dcat", "http://www.w3.org/ns/dcat#"),
        Prefix("dcterms", "http://purl.org/dc/terms/"),
        
        // Social vocabulary
        Prefix("foaf", "http://xmlns.com/foaf/0.1/"),
        
        // Schema vocabulary
        Prefix("schema", "http://schema.org/"),
        
        // Custom vocabulary
        Prefix("ex", "http://example.org/vocab#")
    ]
)

3. Use File-Level Mappings for Consistency

Use file-level mappings when multiple classes share the same prefixes:

@file:Rdf(
    prefixes = [
        Prefix("vcard", "http://www.w3.org/2006/vcard/ns#"),
        Prefix("org", "http://www.w3.org/ns/org#")
    ]
)

// All classes in this file can use vcard: and org: prefixes
@Rdf(iri = "vcard:Individual")
interface Person { /* ... */ }

@Rdf(iri = "org:Organization")
interface Organization { /* ... */ }

4. Validate Prefix Mappings

Ensure your prefix mappings are correct:

// ✅ Good: Verified namespace URIs
@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#"), // Verified
        Prefix("dcterms", "http://purl.org/dc/terms/") // Verified
    ]
)

// ❌ Avoid: Unverified or incorrect URIs
@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://example.org/dcat#"), // Wrong namespace
        Prefix("dcterms", "http://purl.org/dc/terms") // Missing trailing slash
    ]
)

Examples

Complete DCAT Example

@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#"),
        Prefix("dcterms", "http://purl.org/dc/terms/"),
        Prefix("foaf", "http://xmlns.com/foaf/0.1/"),
        Prefix("skos", "http://www.w3.org/2004/02/skos/core#")
    ]
)

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

@Rdf(iri = "dcat:Dataset")
interface Dataset {
    @Rdf(iri = "dcterms:title")
    val title: String
    
    @Rdf(iri = "dcterms:description")
    val description: String
    
    @Rdf(iri = "dcat:distribution")
    val distribution: List<Distribution>
    
    @Rdf(iri = "dcterms:keyword")
    val keywords: List<String>
}

@Rdf(iri = "dcat:Distribution")
interface Distribution {
    @Rdf(iri = "dcterms:title")
    val title: String
    
    @Rdf(iri = "dcat:downloadURL")
    val downloadUrl: String
    
    @Rdf(iri = "dcat:mediaType")
    val mediaType: String
    
    @Rdf(iri = "dcterms:format")
    val format: String
}

@Rdf(iri = "foaf:Agent")
interface Agent {
    @Rdf(iri = "foaf:name")
    val name: String
    
    @Rdf(iri = "foaf:homepage")
    val homepage: String
}

Schema.org Example

@file:Rdf(
    prefixes = [
        Prefix("schema", "http://schema.org/"),
        Prefix("geo", "http://www.w3.org/2003/01/geo/wgs84_pos#")
    ]
)

@Rdf(iri = "schema:Place")
interface Place {
    @Rdf(iri = "schema:name")
    val name: String
    
    @Rdf(iri = "schema:address")
    val address: String
    
    @Rdf(iri = "geo:lat")
    val latitude: Double
    
    @Rdf(iri = "geo:long")
    val longitude: Double
}

@Rdf(iri = "schema:Person")
interface Person {
    @Rdf(iri = "schema:name")
    val name: String
    
    @Rdf(iri = "schema:email")
    val email: String
    
    @Rdf(iri = "schema:worksFor")
    val worksFor: Organization
}

@Rdf(iri = "schema:Organization")
interface Organization {
    @Rdf(iri = "schema:name")
    val name: String
    
    @Rdf(iri = "schema:address")
    val address: String
}

Custom Vocabulary Example

@file:Rdf(
    prefixes = [
        Prefix("ex", "http://example.org/vocab#"),
        Prefix("time", "http://www.w3.org/2006/time#"),
        Prefix("qudt", "http://qudt.org/schema/qudt/")
    ]
)

@Rdf(iri = "ex:Event")
interface Event {
    @Rdf(iri = "ex:name")
    val name: String
    
    @Rdf(iri = "ex:description")
    val description: String
    
    @Rdf(iri = "time:hasBeginning")
    val startTime: String
    
    @Rdf(iri = "time:hasEnd")
    val endTime: String
    
    @Rdf(iri = "ex:location")
    val location: Place
}

@Rdf(iri = "ex:Place")
interface Place {
    @Rdf(iri = "ex:name")
    val name: String
    
    @Rdf(iri = "qudt:latitude")
    val latitude: Double
    
    @Rdf(iri = "qudt:longitude")
    val longitude: Double
}

Troubleshooting

Common Issues

1. Unknown Prefix Error

Error: Unknown prefix: dcat in QName: dcat:Catalog

Solution: Declare the prefix with @file:Rdf(prefixes = …) on the Kotlin file, or with prefixes = […] on the domain @Rdf next to iri:

// ✅ Correct — file-level prefixes
@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#")
    ]
)

package com.example

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

@Rdf(iri = "dcat:Catalog")
interface Catalog

// ❌ Incorrect — missing prefix declaration
@Rdf(iri = "dcat:Catalog")
interface BrokenCatalog

2. Case Sensitivity Issues

Error: Unknown prefix: DCAT in QName: DCAT:Catalog

Solution: Prefix names are case-sensitive. Use consistent casing:

// ✅ Correct
@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#")
    ]
)

package com.example

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

@Rdf(iri = "dcat:Catalog")
interface Catalog

// ❌ Incorrect - case mismatch
@Rdf(iri = "DCAT:Catalog")
interface BrokenCatalog

3. Missing Namespace URI

Error: Prefix annotation missing 'namespace' property

Solution: Ensure both name and namespace are provided:

// ✅ Correct
@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#")
    ]
)

// ❌ Incorrect - missing namespace
@file:Rdf(
    prefixes = [
        Prefix("dcat", "") // Empty namespace
    ]
)

4. Invalid QName Format

Error: Invalid QName format: :Catalog

Solution: QNames must have both prefix and local name:

// ✅ Correct
@Rdf(iri = "dcat:Catalog")

// ❌ Incorrect - missing prefix
@Rdf(iri = ":Catalog")

// ❌ Incorrect - missing local name
@Rdf(iri = "dcat:")

Debugging Tips

1. Verify Prefix Mappings

Check that your prefix mappings are correct:

@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#"), // Should end with #
        Prefix("dcterms", "http://purl.org/dc/terms/") // Should end with /
    ]
)

2. Use Full IRIs for Testing

If QNames aren’t working, try using full IRIs temporarily:

// Test with full IRI
@Rdf(iri = "http://www.w3.org/ns/dcat#Catalog")

// Then convert to QName
@Rdf(iri = "dcat:Catalog")

3. Check Annotation Processing

Ensure KSP is processing your annotations:

// Add logging to see if annotations are processed
@file:Rdf(
    prefixes = [
        Prefix("dcat", "http://www.w3.org/ns/dcat#")
    ]
)
@Rdf(iri = "dcat:Catalog")
interface Catalog {
    // Check build logs for annotation processing messages
}

Conclusion

Prefix mappings and QNames make your Kastor Gen annotations more readable and maintainable. By using well-known prefixes and following best practices, you can create clean, consistent domain interfaces that are easy to understand and modify.

For more information, see: