rdf_core logo

rdf_core

A type-safe, extensible Dart library for RDF data manipulation

pub package build codecov

Type Safety

Strongly-typed IRIs, Literals, Triples, and Graphs ensure correctness and prevent bugs at compile time.

Extensible & Modular

Clean plugin architecture lets you add new codecs with ease.

Blazing Fast

Optimized for performance with zero dependencies besides logging and efficient graph operations.

Standards-Compliant

Implements W3C RDF 1.1 and related specs for maximum interoperability.

πŸš€ Quick Start

import 'package:rdf_core/rdf_core.dart';

// Create a graph manually
final graph = RdfGraph(triples: [
  Triple(
    IriTerm('https://example.org/subject'),
    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');

πŸ’‘ Decode and Encode

// 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); 

πŸ”§ Non-Standard Turtle RDF Support

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

πŸ”„ N-Triples Format

// 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);

πŸ“Š JSON-LD Format

// 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);

πŸ—ΊοΈ API Overview

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)
RdfGraph Collection of RDF triples
RdfGraphCodec Base class for decoding/encoding RdfGraph in various formats
RdfGraphDecoder Base class for decoding RdfGraph
RdfGraphEncoder Base class for encoding RdfGraph
turtle Global convenience variable for Turtle format
jsonldGraph Global convenience variable for JSON-LD format
ntriples Global convenience variable for N-Triples format
rdf Global RdfCore instance with standard codecs