A type-safe, extensible Dart library for RDF data manipulation
Strongly-typed IRIs, Literals, Triples, and Graphs ensure correctness and prevent bugs at compile time.
Clean plugin architecture lets you add new codecs with ease.
Automatic query optimization with lazy indexing provides O(1) subject-based queries. Zero memory cost until first use, transparent performance improvements for existing code.
Implements W3C RDF 1.1 including datasets with named graphs and related specs for maximum interoperability.
Full RDF 1.1 dataset support with named graphs, N-Quads format, and seamless quad/triple conversion.
import 'package:rdf_core/rdf_core.dart';
// Create a graph manually
final graph = RdfGraph(triples: [
Triple(
const IriTerm('https://example.org/subject'),
const IriTerm('https://example.org/predicate'),
LiteralTerm.string('Hello, World!'),
),
]);
// Use global convenience variables
final turtleData = '@prefix ex: . ex:subject ex:predicate "Hello, World!" .';
final graphFromTurtle = turtle.decode(turtleData);
final jsonLdData = '{"@id": "http://example.org/subject", "http://example.org/predicate": "Hello, World!"}';
final graphFromJsonLd = jsonldGraph.decode(jsonLdData);
// Or use the pre-configured RdfCore instance
final rdfGraph = rdf.decode(turtleData, contentType: 'text/turtle');
// New: Query and filter with automatic performance optimization
if (graph.hasTriples(subject: const IriTerm('https://example.org/subject'))) {
print('Found subject in graph!');
}
// New: Create filtered graphs for composition workflows
final filtered = graph.matching(predicate: const IriTerm('https://example.org/predicate'));
final combined = filtered.merge(otherGraph);
// Decode and encode Turtle using the global convenience variable
final doc = '''
@prefix ex: .
@prefix foaf: .
ex:alice foaf:knows ex:bob;
foaf:name "Alice" .
ex:bob foaf:name "Bob" .
''';
final parsed = turtle.decode(doc);
final serialized = turtle.encode(parsed);
// Alternatively, use the pre-configured RdfCore instance
final rdf = RdfCore.withStandardCodecs();
final parsed2 = rdf.decode(doc, contentType: 'text/turtle');
final serialized2 = rdf.encode(parsed2, contentType: 'text/turtle');
// Outputs nearly the same as doc, but we did not provide customPrefixes for the encoder so this time we get the full IRIs for example.com.
// Note that well known prefixes like foaf: are used automatically.
// If we wanted exactly the same, we would have to call it like this:
// final serialized = turtle.encode(parsed, customPrefixes: {'ex': 'http://example.org/'} );
print(serialized);
// Decode documents with non-standard Turtle syntax
import 'package:rdf_core/rdf_core.dart';
// Configure a TurtleCodec with specific parsing flags
final turtleCodec = TurtleCodec(
decoderOptions: TurtleDecoderOptions(
parsingFlags: {
TurtleParsingFlag.allowDigitInLocalName, // Allow local names with digits (e.g., "resource123")
TurtleParsingFlag.allowMissingDotAfterPrefix, // Allow prefix declarations without trailing dot
TurtleParsingFlag.allowIdentifiersWithoutColon, // Treat terms without colon as IRIs resolved against base URI
TurtleParsingFlag.allowMissingFinalDot, // Allow missing dot at end of triple
}
)
);
// Create an RDF Core instance with the custom codec
final rdf = RdfCore.withCodecs(codecs: [turtleCodec]);
// Decode a non-standard document that would fail with strict parsing
final nonStandardTurtle = '''
@base .
@prefix ex: // Missing dot after prefix declaration
ex:resource123 a Type . // Digit in local name (123) and "Type" without prefix resolves to
''';
final graph = rdf.decode(nonStandardTurtle, contentType: 'text/turtle');
// Decode and encode N-Triples using the global convenience variable
final ntriplesDoc = '''
"Alice"@en .
.
"Bob" .
''';
// Option 1: Use the global convenience variable
final graph = ntriples.decode(ntriplesDoc);
final encodedNTriples = ntriples.encode(graph);
// Option 2: Use the pre-configured RdfCore instance
final rdf = RdfCore.withStandardCodecs();
// Decode N-Triples explicitly by content type
final graph2 = rdf.decode(ntriplesDoc, contentType: 'application/n-triples');
// Or let the library auto-detect the format
final autoDetected = rdf.decode(ntriplesDoc);
// Encode to N-Triples
final encodedNTriples2 = rdf.encode(graph2, contentType: 'application/n-triples');
print(encodedNTriples2);
// Convert between formats - decode N-Triples and encode to Turtle
final encodedTurtle = rdf.encode(graph2, contentType: 'text/turtle');
print(encodedTurtle);
// Create and work with RDF datasets containing named graphs
import 'package:rdf_core/rdf_core.dart';
// Create quads with graph context
final alice = const IriTerm('http://example.org/alice');
final bob = const IriTerm('http://example.org/bob');
final foafName = const IriTerm('http://xmlns.com/foaf/0.1/name');
final foafKnows = const IriTerm('http://xmlns.com/foaf/0.1/knows');
final peopleGraph = const IriTerm('http://example.org/graphs/people');
final quads = [
Quad(alice, foafName, LiteralTerm.string('Alice')), // default graph
Quad(alice, foafKnows, bob, peopleGraph), // named graph
Quad(bob, foafName, LiteralTerm.string('Bob'), peopleGraph), // named graph
];
// Create dataset from quads
final dataset = RdfDataset.fromQuads(quads);
// Option 1: Use the global convenience variable
final nquadsData = nquads.encode(dataset);
// Option 2: Use the pre-configured RdfCore instance
final rdf = RdfCore.withStandardCodecs();
final nquadsData2 = rdf.encodeDataset(dataset, contentType: 'application/n-quads');
print('N-Quads output:\n$nquadsData');
// Decode N-Quads data back to dataset
final decodedDataset = nquads.decode(nquadsData);
// Access default and named graphs
print('Default graph has ${decodedDataset.defaultGraph.triples.length} triples');
print('Dataset has ${decodedDataset.namedGraphs.length} named graphs');
// Automatic query optimization with lazy indexing
import 'package:rdf_core/rdf_core.dart';
final john = const IriTerm('http://example.org/john');
final jane = const IriTerm('http://example.org/jane');
final foafName = const IriTerm('http://xmlns.com/foaf/0.1/name');
final foafKnows = const IriTerm('http://xmlns.com/foaf/0.1/knows');
final graph = RdfGraph(triples: [
Triple(john, foafName, LiteralTerm.string('John Smith')),
Triple(john, foafKnows, jane),
Triple(jane, foafName, LiteralTerm.string('Jane Doe')),
]);
// Efficient boolean queries - O(1) with automatic indexing
if (graph.hasTriples(subject: john, predicate: foafName)) {
print('John has a name'); // β
Fast lookup
}
// Create filtered graphs for composition
final johnGraph = graph.matching(subject: john); // John's info
final relationships = graph.matching(predicate: foafKnows); // All relationships
// Chain operations for powerful workflows
final result = graph
.matching(subject: john) // Start with John's data
.merge(additionalData) // Add more information
.matching(predicate: foafKnows); // Filter to relationships only
// Performance: First query builds index, subsequent queries are O(1)
final nameTriples = graph.findTriples(subject: john); // Index created here
final hasName = graph.hasTriples(subject: john); // Uses existing index - very fast!
// Decode and encode JSON-LD using the global convenience variable
final jsonLdDoc = '''
{
"@context": {
"name": "http://xmlns.com/foaf/0.1/name",
"knows": {
"@id": "http://xmlns.com/foaf/0.1/knows",
"@type": "@id"
},
"Person": "http://xmlns.com/foaf/0.1/Person"
},
"@id": "http://example.org/alice",
"@type": "Person",
"name": "Alice",
"knows": [
{
"@id": "http://example.org/bob",
"@type": "Person",
"name": "Bob"
}
]
}
''';
// Option 1: Use the global convenience variable
final graph = jsonldGraph.decode(jsonLdDoc);
final encodedJsonLd = jsonldGraph.encode(graph);
// Option 2: Use the pre-configured RdfCore instance
final rdf = RdfCore.withStandardCodecs();
// Decode JSON-LD by content type
final graph2 = rdf.decode(jsonLdDoc, contentType: 'application/ld+json');
// Encode back to JSON-LD
final encodedJsonLd2 = rdf.encode(graph2, contentType: 'application/ld+json');
print(encodedJsonLd2);
// Convert between formats - decode JSON-LD and encode to Turtle
final encodedTurtle = rdf.encode(graph2, contentType: 'text/turtle');
print(encodedTurtle);
For full RDF Dataset Canonicalization (RDF-CANON) compliance, use the separate rdf_canonicalization package.
dart pub add rdf_canonicalization
import 'package:rdf_core/rdf_core.dart';
import 'package:rdf_canonicalization/rdf_canonicalization.dart';
// Create a dataset with blank nodes
final dataset = RdfDataset.fromQuads([
Quad(BlankNodeTerm(), const IriTerm('http://example.org/name'), LiteralTerm.string('Alice')),
Quad(BlankNodeTerm(), const IriTerm('http://example.org/name'), LiteralTerm.string('Bob')),
]);
// Canonicalize the dataset according to RDF-CANON spec
final canonicalNQuads = canonicalize(dataset);
print('Canonical N-Quads:\n$canonicalNQuads');
// Or canonicalize a single graph
final graph = RdfGraph(triples: [
Triple(BlankNodeTerm(), const IriTerm('http://example.org/name'), LiteralTerm.string('Example')),
]);
final canonicalGraph = canonicalizeGraph(graph);
print('Canonical graph:\n$canonicalGraph');
// Test if two datasets are semantically equivalent
final dataset2 = RdfDataset.fromQuads([
Quad(BlankNodeTerm(), const IriTerm('http://example.org/name'), LiteralTerm.string('Bob')),
Quad(BlankNodeTerm(), const IriTerm('http://example.org/name'), LiteralTerm.string('Alice')),
]);
final isEquivalent = isIsomorphic(dataset, dataset2);
print('Are datasets equivalent? $isEquivalent');
Note: The canonical
option in this library's N-Quads and N-Triples encoders provides basic deterministic output but does not implement the full RDF Dataset Canonicalization specification. For complete RDF-CANON compliance (including proper blank node canonicalization), use the rdf_canonicalization
package.
Type | Description |
---|---|
IriTerm |
Represents an IRI (Internationalized Resource Identifier) |
LiteralTerm |
Represents an RDF literal value |
BlankNodeTerm |
Represents a blank node |
Triple |
Atomic RDF statement (subject, predicate, object) |
Quad |
RDF statement with optional graph context (subject, predicate, object, graph) |
RdfGraph |
Collection of RDF triples with automatic query optimization |
RdfDataset |
Collection of named graphs plus a default graph |
RdfNamedGraph |
A named graph pair (name + graph) |
hasTriples() |
Check if matching triples exist (boolean result, O(1) optimized) |
matching() |
Create filtered graphs for composition and chaining workflows |
RdfGraphCodec |
Base class for decoding/encoding RdfGraph in various formats |
RdfDatasetCodec |
Base class for decoding/encoding RdfDataset in various formats |
RdfGraphDecoder |
Base class for decoding RdfGraph |
RdfGraphEncoder |
Base class for encoding RdfGraph |
RdfDatasetDecoder |
Base class for decoding RdfDataset |
RdfDatasetEncoder |
Base class for encoding RdfDataset |
turtle |
Global convenience variable for Turtle format |
jsonldGraph |
Global convenience variable for JSON-LD format |
ntriples |
Global convenience variable for N-Triples format |
nquads |
Global convenience variable for N-Quads format |
rdf |
Global RdfCore instance with standard codecs |