RDF Core

Parse, serialize, and query RDF graphs and datasets.
W3C RDF 1.1 compliant core library for Dart.

Getting Started

Install the package

dart pub add locorda_rdf_core
import 'package:locorda_rdf_core/core.dart';

void main() {
  final turtleData = '''
    @prefix foaf: <http://xmlns.com/foaf/0.1/> .
    <http://example.org/alice> foaf:name "Alice"@en .
  ''';

  final graph = turtle.decode(turtleData);

  print('Triples: ${graph.triples.length}'); // Triples: 1
}
import 'package:locorda_rdf_core/core.dart';

void main() {
  final turtleData = '''
    @prefix ex: <http://example.org/> .
    @prefix foaf: <http://xmlns.com/foaf/0.1/> .
    
    ex:Alice foaf:knows ex:Bob .
    ex:Bob foaf:knows ex:Charlie .
  ''';

  final graph = turtle.decode(turtleData);

  // Find all triples where the predicate is foaf:knows
  final knowsTriples =
      graph.findTriples(predicate: IriTerm('http://xmlns.com/foaf/0.1/knows'));

  print('People who know someone:');
  for (final triple in knowsTriples) {
    print('  ${triple.subject}');
  }

  // Add new triple (creates new immutable graph)
  final updatedGraph = graph.withTriple(Triple(
    IriTerm('http://example.org/Bob'),
    IriTerm('http://xmlns.com/foaf/0.1/knows'),
    IriTerm('http://example.org/David'),
  ));

  print('\nTotal triples: ${updatedGraph.triples.length}');
}
import 'package:locorda_rdf_core/core.dart';

void main() {
  // 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);

  // Encode to N-Quads
  final nquadsData = nquads.encode(dataset);
  print('N-Quads output:');
  print(nquadsData);

  // Decode N-Quads 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');

  for (final namedGraph in decodedDataset.namedGraphs) {
    print(
        'Named graph ${namedGraph.name} has ${namedGraph.graph.triples.length} triples');
  }
}
import 'package:locorda_rdf_core/core.dart';

void main() {
  // Example: Decode a JSON-LD document
  final jsonLdData = '''
  {
    "@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"
      }
    ]
  }
  ''';

  // Using the convenience global variable
  final graph = jsonldGraph.decode(jsonLdData);

  // Print decoded triples
  print('Decoded ${graph.triples.length} triples:');
  for (final triple in graph.triples) {
    print('${triple.subject} ${triple.predicate} ${triple.object}');
  }

  // Encode the graph back to JSON-LD
  final serialized = jsonldGraph.encode(graph);
  print('\nEncoded JSON-LD:');
  print(serialized);
}
import 'package:locorda_rdf_core/core.dart';

void main() {
  // Create individual triples
  final subject = const IriTerm('http://example.org/alice');
  final predicate = const IriTerm('http://xmlns.com/foaf/0.1/name');
  final object = LiteralTerm.string('Alice');
  final triple = Triple(subject, predicate, object);

  // Create graph with triples
  final graph = RdfGraph(triples: [triple]);

  print('Created graph with ${graph.triples.length} triple(s)');
  print('\nTriples:');
  for (final t in graph.triples) {
    print('  ${t.subject} ${t.predicate} ${t.object}');
  }

  // Add more triples using withTriple
  final age = const IriTerm('http://xmlns.com/foaf/0.1/age');
  final updatedGraph = graph.withTriple(
    Triple(subject, age, LiteralTerm.integer(30)),
  );

  print('\nUpdated graph has ${updatedGraph.triples.length} triples');

  // Encode to Turtle
  print('\nTurtle format:');
  print(turtle.encode(updatedGraph));
}
import 'package:locorda_rdf_core/core.dart';

void main() {
  // Create two separate graphs
  final graph1 = RdfGraph(triples: [
    Triple(
      const IriTerm('http://example.org/alice'),
      const IriTerm('http://xmlns.com/foaf/0.1/name'),
      LiteralTerm.string('Alice'),
    ),
    Triple(
      const IriTerm('http://example.org/alice'),
      const IriTerm('http://xmlns.com/foaf/0.1/age'),
      LiteralTerm.integer(30),
    ),
  ]);

  final graph2 = RdfGraph(triples: [
    Triple(
      const IriTerm('http://example.org/bob'),
      const IriTerm('http://xmlns.com/foaf/0.1/name'),
      LiteralTerm.string('Bob'),
    ),
    Triple(
      const IriTerm('http://example.org/alice'),
      const IriTerm('http://xmlns.com/foaf/0.1/knows'),
      const IriTerm('http://example.org/bob'),
    ),
  ]);

  print('Graph 1 has ${graph1.triples.length} triples');
  print('Graph 2 has ${graph2.triples.length} triples');

  // Merge graphs
  final merged = graph1.merge(graph2);
  print('\nMerged graph has ${merged.triples.length} triples');

  // Query for specific patterns
  final alice = const IriTerm('http://example.org/alice');
  final aliceTriples = merged.findTriples(subject: alice);

  print('\nAll triples about Alice:');
  for (final triple in aliceTriples) {
    print('  ${triple.predicate} -> ${triple.object}');
  }

  // Check if triple exists
  final foafKnows = const IriTerm('http://xmlns.com/foaf/0.1/knows');
  if (merged.hasTriples(subject: alice, predicate: foafKnows)) {
    print('\nAlice knows someone!');
  }

  // Create filtered graph
  final aliceGraph = merged.matching(subject: alice);
  print(
      '\nFiltered graph with only Alice has ${aliceGraph.triples.length} triples');
}
import 'package:locorda_rdf_core/core.dart';

void main() {
  // Sample data in Turtle format
  final turtleData = '''
    @prefix foaf: <http://xmlns.com/foaf/0.1/> .
    <http://example.org/alice> foaf:name "Alice" ;
                                foaf:age 30 .
  ''';

  // Using the preconfigured RdfCore instance
  // Decode with explicit content type
  print('1. Decoding Turtle with RdfCore:');
  final graph = rdf.decode(turtleData, contentType: 'text/turtle');
  print('   Decoded ${graph.triples.length} triples');

  // Encode to different format
  print('\n2. Encoding to JSON-LD:');
  final jsonld = rdf.encode(graph, contentType: 'application/ld+json');
  print(jsonld);

  // Encode to N-Triples
  print('\n3. Encoding to N-Triples:');
  final ntriples = rdf.encode(graph, contentType: 'application/n-triples');
  print(ntriples);

  // Working with datasets and N-Quads
  print('\n4. Working with Datasets:');
  final quadsData = '''
    <http://example.org/alice> <http://xmlns.com/foaf/0.1/name> "Alice" .
    <http://example.org/bob> <http://xmlns.com/foaf/0.1/name> "Bob" <http://example.org/graph1> .
  ''';

  final dataset =
      rdf.decodeDataset(quadsData, contentType: 'application/n-quads');
  print('   Default graph: ${dataset.defaultGraph.triples.length} triples');
  print('   Named graphs: ${dataset.namedGraphs.length}');

  // Encode dataset back to N-Quads
  final encodedDataset =
      rdf.encodeDataset(dataset, contentType: 'application/n-quads');
  print('\n5. Encoded Dataset:');
  print(encodedDataset);

  // Format auto-detection (when contentType is omitted)
  print('\n6. Auto-detection:');
  final autoDetected = rdf.decode(turtleData); // Automatically detects Turtle
  print('   Auto-detected and decoded ${autoDetected.triples.length} triples');
}
import 'package:locorda_rdf_core/core.dart';

void main() {
  // Example 1: Using individual codec instances directly
  print('1. Using codec instances directly:');
  final turtleData = '''
    @prefix foaf: <http://xmlns.com/foaf/0.1/> .
    <http://example.org/alice> foaf:name "Alice" .
  ''';

  // Each format has a global codec instance
  final graph1 = turtle.decode(turtleData);
  final asJsonLd = jsonldGraph.encode(graph1);
  print('   Encoded as JSON-LD:\n$asJsonLd');

  // Example 2: Creating a custom RdfCore instance
  print('\n2. Creating custom RdfCore with specific codecs:');

  // Create RdfCore with only Turtle and N-Triples support
  final customRdf = RdfCore.withCodecs(codecs: [
    TurtleCodec(),
    NTriplesCodec(),
  ]);

  final graph2 = customRdf.decode(turtleData, contentType: 'text/turtle');
  final asNTriples =
      customRdf.encode(graph2, contentType: 'application/n-triples');
  print('   Encoded as N-Triples:\n$asNTriples');

  // Example 3: Customizing codec options
  print('\n3. Using codecs with custom options:');

  // Create a Turtle codec with custom parsing options
  final strictTurtle = TurtleCodec(
    decoderOptions: TurtleDecoderOptions(
      parsingFlags: {
        TurtleParsingFlag.allowDigitInLocalName,
      },
    ),
  );

  final customRdfCore = RdfCore.withCodecs(codecs: [strictTurtle]);

  // Use the custom codec
  final strictData =
      '@prefix ex: <http://example.org/> . ex:resource123 a ex:Type .';
  final graph3 = customRdfCore.decode(strictData, contentType: 'text/turtle');
  print('   Parsed ${graph3.triples.length} triples with custom options');

  // Example 4: Standard codecs (recommended for most use cases)
  print('\n4. Using standard codecs (includes all formats):');
  final standardRdf = RdfCore.withStandardCodecs();

  // Supports Turtle, N-Triples, N-Quads, and JSON-LD out of the box
  final graph4 = standardRdf.decode(turtleData, contentType: 'text/turtle');
  print('   Standard RdfCore decoded ${graph4.triples.length} triples');

  // Can encode to any standard format
  final formats = [
    'text/turtle',
    'application/n-triples',
    'application/ld+json'
  ];
  print('\n   Available standard formats:');
  for (final format in formats) {
    final encoded = standardRdf.encode(graph4, contentType: format);
    print('   - $format: ${encoded.split('\n').length} lines');
  }
}

What Can You Build?

πŸ—„οΈ Knowledge Graphs

Build semantic knowledge bases with typed relationships. Perfect for domain modeling, ontologies, and linked data applications.

πŸ”„ Data Integration

Merge data from multiple sources into a unified graph. Transform between formats (Turtle ↔ JSON-LD ↔ N-Triples) seamlessly.

οΏ½ In-Memory Graph Queries

Graph queries with O(1) subject lookups. Automatic indexing makes complex queries efficient for datasets that fit in RAM.

Key Features

⚑ Automatic Performance Optimization

Lazy indexing provides O(1) subject-based queries with zero memory cost until first use. Queries benefit from transparent performance improvements.

🎯 Developer-Friendly API

Global convenience variables (turtle, jsonld, ntriples) make parsing trivial. Fluent APIs for graph composition and chaining.

πŸ“„ Multiple Formats

Parse and serialize Turtle, N-Triples, N-Quads, and JSON-LD. W3C RDF 1.1 compliant.

πŸ“Š Full Dataset Support

Work with named graphs and datasets. Complete quad support for N-Quads with RDF 1.1 Dataset compliance.

πŸ”· Type-Safe

Strongly-typed Dart APIs with full IDE support and autocompletion. Catch errors at compile time.

βš™οΈ Zero Dependencies

No external dependencies except logging. Clean, maintainable codebase.

Ready to Start?

Read the documentation or explore other RDF packages.