RDF Mapper Annotations

Overview of key annotations for declarative RDF mapping.
Define your mappings, let the generator do the work.

locorda_rdf_mapper_annotations provides the annotation system for declarative RDF mapping. Use it with locorda_rdf_mapper_generator to automatically generate type-safe mapping code from your annotated classes.

This page provides an overview of the most important annotations. For exhaustive documentation, see the API documentation on pub.dev. For getting started and complete examples, visit the mapper guide.

Class Annotations

@RdfGlobalResource

For entities with globally unique IRIs (subjects in RDF triples).

import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_terms_schema/schema.dart';

/// Global resources have globally unique IRIs.
/// They become subjects in RDF triples.
@RdfGlobalResource(
  SchemaBook.classIri,
  IriStrategy('https://example.org/books/{isbn}'),
)
class Book {
  @RdfIriPart()
  final String isbn;

  @RdfProperty(SchemaBook.name)
  final String title;

  Book({required this.isbn, required this.title});
}

@RdfLocalResource

For child entities that exist only in relation to a parent (blank nodes).

import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_terms_schema/schema.dart';

/// Local resources exist only in relation to a parent.
/// They're represented as blank nodes in RDF.
@RdfLocalResource(SchemaChapter.classIri)
class Chapter {
  @RdfProperty(SchemaChapter.name)
  final String title;

  @RdfProperty(SchemaChapter.position)
  final int number;

  Chapter({required this.title, required this.number});
}

@RdfLiteral

For classes representing simple values with validation or custom formats.

import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_core/core.dart';

/// Custom literal type example with @RdfValue() for simple value extraction
@RdfLiteral()
class ISBN {
  /// The ISBN value that will be serialized as literal
  @RdfValue()
  final String value;

  ISBN(this.value) {
    if (!RegExp(r'^(?:\d{9}[\dX]|\d{13})$').hasMatch(value)) {
      throw ArgumentError('Invalid ISBN format');
    }
  }
}

/// Custom literal with formatted string representation using custom conversion methods
@RdfLiteral.custom(
  toLiteralTermMethod: 'formatCelsius',
  fromLiteralTermMethod: 'parse',
  datatype: IriTerm('http://example.org/temperature'),
)
class Temperature {
  final double celsius;

  Temperature(this.celsius);

  /// Custom formatting method for serialization
  LiteralContent formatCelsius() => LiteralContent('$celsius°C');

  /// Static parsing method for deserialization
  static Temperature parse(LiteralContent term) {
    return Temperature(double.parse(term.value.replaceAll('°C', '')));
  }
}

@RdfIri

For classes and enums representing IRIs: identifiers (ISBN, DOI, URIs), template-based IRI generation, and controlled vocabularies.

import 'package:locorda_rdf_mapper_annotations/annotations.dart';

/// Template-based IRI for standardized identifiers
@RdfIri('urn:isbn:{value}')
class ISBN {
  @RdfIriPart() // 'value' is inferred from property name
  final String value;

  ISBN(this.value);
  // Serialized as: urn:isbn:9780261102217
}

/// Direct value as IRI (no template)
@RdfIri()
class AbsoluteUri {
  @RdfIriPart()
  final String uri;

  AbsoluteUri(this.uri);
  // Uses the value directly as IRI: 'https://example.org/resource/123'
}

/// Multi-part IRI with context variables
@RdfIri('{+baseUri}/collections/{collection}/{item}')
class CollectionItem {
  @RdfIriPart('collection')
  final String collection;

  @RdfIriPart('item')
  final String item;

  CollectionItem(this.collection, this.item);
  // With baseUri='https://api.example.org':
  // → https://api.example.org/collections/books/item-123
}

Enum example: Also works with enums for controlled vocabularies.

import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_terms_schema/schema.dart';

/// Enum mapped to IRIs using @RdfIri annotation with template.
/// Use @RdfEnumValue to customize individual enum values.
@RdfIri('https://schema.org/{value}')
enum BookFormat {
  @RdfEnumValue('Hardcover')
  hardcover,

  @RdfEnumValue('Paperback')
  paperback,

  @RdfEnumValue('EBook')
  ebook,
}

@RdfGlobalResource(
  SchemaBook.classIri,
  IriStrategy('https://example.org/books/{isbn}'),
)
class Book {
  @RdfIriPart()
  final String isbn;

  @RdfProperty(SchemaBook.name)
  final String title;

  @RdfProperty(SchemaBook.bookFormat)
  final BookFormat format;

  Book({
    required this.isbn,
    required this.title,
    required this.format,
  });
}

Property Annotations

@RdfProperty

Maps a Dart property to an RDF predicate.

import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_terms_schema/schema.dart';

/// @RdfProperty maps fields to RDF predicates.
@RdfGlobalResource(
  SchemaBook.classIri,
  IriStrategy('https://example.org/books/{isbn}'),
)
class Book {
  @RdfIriPart()
  final String isbn;

  /// Simple property mapping
  @RdfProperty(SchemaBook.name)
  final String title;

  /// Optional properties (nullable fields)
  @RdfProperty(SchemaBook.author)
  final String? author;

  /// Collections are automatically handled
  @RdfProperty(SchemaBook.keywords)
  final List<String> tags;

  Book({
    required this.isbn,
    required this.title,
    this.author,
    this.tags = const [],
  });
}

@RdfUnmappedTriples

Captures unmapped triples for lossless round-trip conversion.

import 'package:locorda_rdf_core/core.dart';
import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_terms_schema/schema.dart';

/// @RdfUnmappedTriples preserves triples not mapped to fields.
/// Enables lossless round-trip conversion.
@RdfGlobalResource(
  SchemaBook.classIri,
  IriStrategy('https://example.org/books/{isbn}'),
)
class Book {
  @RdfIriPart()
  final String isbn;

  @RdfProperty(SchemaBook.name)
  final String title;

  /// All unmapped triples are stored here
  @RdfUnmappedTriples()
  final RdfGraph unmappedProperties;

  Book({
    required this.isbn,
    required this.title,
    RdfGraph? unmappedProperties,
  }) : unmappedProperties = unmappedProperties ?? RdfGraph();
}

IRI Strategies

IRI strategies define how object properties are mapped to RDF resource identifiers.

IriStrategy Examples

Template-based strategies with placeholders and runtime context variables.

import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_terms_schema/schema.dart';

/// IriStrategy with template placeholders.
/// Use @RdfIriPart() to mark fields that fill the template.
@RdfGlobalResource(
  SchemaPerson.classIri,
  IriStrategy('https://example.org/users/{username}'),
)
class User {
  @RdfIriPart()
  final String username;

  @RdfProperty(SchemaPerson.name)
  final String fullName;

  User({required this.username, required this.fullName});
}

/// IriStrategy with baseUri parameter.
/// Useful for dynamic base URIs.
@RdfGlobalResource(
  SchemaProduct.classIri,
  IriStrategy('{+baseUri}/products/{id}'),
)
class Product {
  @RdfIriPart()
  final String id;

  @RdfProperty(SchemaProduct.name)
  final String name;

  Product({required this.id, required this.name});
}

Collections Support

Lists, Sets, and RDF Lists

Automatic handling of Dart collections with RDF-specific collection types.

import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_terms_schema/schema.dart';

/// Collections (List, Set) are automatically supported.
@RdfGlobalResource(
  SchemaBook.classIri,
  IriStrategy('https://example.org/books/{isbn}'),
)
class Book {
  @RdfIriPart()
  final String isbn;

  @RdfProperty(SchemaBook.name)
  final String title;

  /// List of strings
  @RdfProperty(SchemaBook.keywords)
  final List<String> tags;

  /// Set of strings (no duplicates)
  @RdfProperty(SchemaBook.author)
  final Set<String> authors;

  /// RDF List (ordered collection)
  @RdfProperty(SchemaBook.hasPart, collection: rdfList)
  final List<Chapter> chapters;

  Book({
    required this.isbn,
    required this.title,
    this.tags = const [],
    this.authors = const {},
    this.chapters = const [],
  });
}

@RdfLocalResource(SchemaChapter.classIri)
class Chapter {
  @RdfProperty(SchemaChapter.name)
  final String title;

  Chapter({required this.title});
}

Map Entries

Custom entry classes for Map support with @RdfMapEntry annotation.

import 'package:locorda_rdf_mapper_annotations/annotations.dart';
import 'package:locorda_rdf_terms_schema/schema.dart';

/// Maps require custom entry classes with @RdfMapEntry.
@RdfGlobalResource(
  SchemaBook.classIri,
  IriStrategy('https://example.org/books/{isbn}'),
)
class Book {
  @RdfIriPart()
  final String isbn;

  @RdfProperty(SchemaBook.name)
  final String title;

  /// Map of language codes to translated titles
  @RdfProperty(SchemaBook.review)
  @RdfMapEntry(TranslationEntry)
  final Map<String, String> translations;

  Book({
    required this.isbn,
    required this.title,
    this.translations = const {},
  });
}

/// Entry class for Map<String, String>
@RdfLocalResource(SchemaReview.classIri)
class TranslationEntry {
  @RdfMapKey()
  @RdfProperty(SchemaReview.inLanguage)
  final String language;

  @RdfMapValue()
  @RdfProperty(SchemaReview.reviewBody)
  final String text;

  TranslationEntry({required this.language, required this.text});
}

Ready to Use?

Go back to the full guide or explore the API docs.