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() {
  // Create individual triples
  final alice = const IriTerm('http://example.org/alice');
  final foafName = const IriTerm('http://xmlns.com/foaf/0.1/name');
  final foafAge = const IriTerm('http://xmlns.com/foaf/0.1/age');

  final triple = Triple(alice, foafName, LiteralTerm.string('Alice'));

  // Create graph from triples
  final graph = RdfGraph(triples: [triple]);
  print('Created graph with ${graph.triples.length} triple(s)');

  // Add more triples (immutable - returns new graph)
  final updatedGraph = graph.withTriple(
    Triple(alice, foafAge, LiteralTerm.integer(30)),
  );
  print('Updated graph has ${updatedGraph.triples.length} triples');

  // Note: For parsing, use turtle.decode(data) instead
  print('\nTurtle output:');
  print(turtle.encode(updatedGraph));
}
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() {
  // Start with a graph
  final alice = const IriTerm('http://example.org/alice');
  final bob = const IriTerm('http://example.org/bob');
  final charlie = const IriTerm('http://example.org/charlie');
  final foafKnows = const IriTerm('http://xmlns.com/foaf/0.1/knows');

  final graph = RdfGraph(triples: [
    Triple(alice, foafKnows, bob),
    Triple(bob, foafKnows, charlie),
  ]);

  // Query: Find all "knows" relationships
  final knowsTriples = graph.findTriples(predicate: foafKnows);

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

  // Update: Add new triple (returns new immutable graph)
  final updatedGraph = graph.withTriple(Triple(alice, foafKnows, charlie));

  print('\nTotal triples: ${updatedGraph.triples.length}');

  // Check existence
  if (updatedGraph.hasTriples(subject: alice, object: charlie)) {
    print('Alice knows Charlie!');
  }
}
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() {
  final turtleData = '''
    @prefix foaf: <http://xmlns.com/foaf/0.1/> .
    <http://example.org/alice> foaf:name "Alice" ;
                                foaf:age 30 .
  ''';

  // Parse Turtle
  final graph = rdf.decode(turtleData, contentType: 'text/turtle');
  print('Parsed ${graph.triples.length} triples');

  // Convert to JSON-LD
  final jsonld = rdf.encode(graph, contentType: 'application/ld+json');
  print('\nJSON-LD:\n$jsonld');

  // Convert to N-Triples
  final ntriples = rdf.encode(graph, contentType: 'application/n-triples');
  print('\nN-Triples:\n$ntriples');
}
// ignore_for_file: unused_local_variable

import 'package:locorda_rdf_core/core.dart';
// import 'package:locorda_rdf_xml/xml.dart';

void main() {
  // Example 1: Customize Turtle parsing for SPARQL-style syntax
  print('1. Custom Turtle codec for SPARQL-style files:');
  final sparqlStyleTurtle = TurtleCodec(
    decoderOptions: TurtleDecoderOptions(
      parsingFlags: {
        TurtleParsingFlag.allowMissingDotAfterPrefix,
        TurtleParsingFlag.allowPrefixWithoutAtSign,
      },
    ),
  );

  final customRdf = RdfCore.withCodecs(codecs: [
    sparqlStyleTurtle,
    NTriplesCodec(),
  ]);

  // Parse SPARQL-style Turtle (no @ before PREFIX, no . after prefix)
  final sparqlData = '''
    PREFIX foaf: <http://xmlns.com/foaf/0.1/>
    <http://example.org/alice> foaf:name "Alice" .
  ''';

  final graph = customRdf.decode(sparqlData, contentType: 'text/turtle');
  print('   Parsed ${graph.triples.length} triples from SPARQL-style syntax\n');

  // Example 2: Add RDF/XML support via extension package
  print('2. Extending with RDF/XML support:');
  final xmlRdf = RdfCore.withCodecs(codecs: [
    TurtleCodec(),
    NTriplesCodec(),
    // RdfXmlCodec(), // from locorda_rdf_xml package
  ]);

  final xmlData = '''
    <?xml version="1.0"?>
    <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
             xmlns:foaf="http://xmlns.com/foaf/0.1/">
      <rdf:Description rdf:about="http://example.org/alice">
        <foaf:name>Alice</foaf:name>
      </rdf:Description>
    </rdf:RDF>
  ''';

  // final xmlGraph = xmlRdf.decode(xmlData, contentType: 'application/rdf+xml');
  // print('   Parsed ${xmlGraph.triples.length} triples from RDF/XML');

  print('   Add locorda_rdf_xml to pubspec.yaml for RDF/XML support');
  print('   Extensible architecture: any codec can be added!');
}

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.

Yeah fine, but:

πŸ’­ "I want to work with Person and Book, not Triples"

β†’ Use mapper (annotations + generator)

Define your domain model with annotations. The generator creates bidirectional mappings between your classes and RDF.

πŸ“„ "Do you support XML?"

β†’ Yes, via xml

Full RDF/XML encoder and decoder for legacy format support and interoperability with older systems.

😩 "Typing IRIs is hell"

β†’ Use terms and vocabularies

Type-safe constants for Schema.org, FOAF, Dublin Core, and more. Never typo a property URI again.

πŸ” "Can you canonicalize?"

β†’ Yes, canonicalization

RDF-CANON (RDFC-1.0) support for canonical serialization, reliable graph comparison, and cryptographic signing.

Ready to Start?

Read the documentation or explore other RDF packages.