RDF Parser/Serializer Implementation for Round-Trip Support in Ontology-Playground
The RDF parser/serializer achieves round-trip support through coupled modules in src/lib/rdf/serializer.ts and src/lib/rdf/parser.ts that use custom OWL annotations to preserve type metadata, identifiers, and relationship attributes during conversion between TypeScript interfaces and RDF/XML.
Ontology-Playground stores ontologies as TypeScript interfaces (Ontology, EntityType, Property, Relationship) and provides bidirectional conversion to standard RDF/XML (OWL) format. The implementation ensures lossless data transfer by embedding custom annotation properties that the parser reads back to reconstruct the exact internal model. This round-trip capability is verified by the test suite in src/lib/rdf/roundtrip.test.ts.
Core Architecture
The round-trip system relies on two tightly coupled modules that mirror each other’s logic:
src/lib/rdf/serializer.ts: Exports the internal TypeScript model to RDF/XML by generating OWL classes, datatype properties, and object properties with deterministic URIs and custom annotations.src/lib/rdf/parser.ts: Imports RDF/XML by parsing the document withDOMParser, reconstructingEntityTypeinstances from OWL classes, and restoring properties using stored metadata annotations.
These modules share a common URI derivation strategy via deriveBaseUri, which normalizes the ontology name into a URL-safe slug (http://example.org/ontology/<slug>/). This ensures that localNameFromUri can reliably map RDF resources back to original entities during import.
How Round-Trip Conversion Works
The implementation preserves model fidelity through five specific mechanisms:
-
Stable Base URI Derivation: The
deriveBaseUrifunction creates a consistent namespace from the ontology name, ensuring every exported resource uses a predictable URI that the parser can resolve back to the original entity ID. -
Custom Type Annotations: While standard RDF uses XSD datatypes, Ontology-Playground stores the original property type in
ont:propertyTypeand enum values inont:enumValues. The parser reads these custom elements first, falling back toXSD_TO_TYPEmappings only when annotations are absent. -
Identifier Flag Preservation: The serializer emits
ont:isIdentifierfor primary key properties. The parser detects this annotation and sets the correspondingisIdentifierboolean on the reconstructedPropertyobject. -
Relationship Attribute Reassembly: Attributes attached to relationships are serialized as separate
owl:DatatypePropertyelements with the annotationont:relationshipAttributeOf. During parsing, these are grouped back into theRelationship.attributesarray using thefromandtoentity mappings stored inont:fromEntityIdandont:toEntityId. -
Data Binding Retention:
ont:DataBindingelements capture external data-source mappings includingsource,table, andcolumnMapping. The parser detects these elements and reconstructs theDataBinding[]array alongside the ontology model.
Implementation Details by Module
Serializer (src/lib/rdf/serializer.ts)
The serializer builds an XML document following OWL/RDF syntax. It processes each EntityType to emit an owl:Class with annotations for ont:icon and ont:color. For properties, it generates owl:DatatypeProperty elements with:
ont:propertyTypeto store the original TypeScript typeont:isIdentifierfor primary key flagsont:unitfor measurement unitsont:enumValuesfor enumeration constraints
Relationships become owl:ObjectProperty elements with rdf:domain and rdf:range derived from entity IDs, or explicit ont:fromEntityId and ont:toEntityId annotations when domain/range are insufficient.
Parser (src/lib/rdf/parser.ts)
The parser validates the XML structure using DOMParser, then walks the OWL elements to reconstruct the model:
- Reads
rdfs:labelandrdfs:commentfor ontology metadata - Extracts
EntityTypedetails from<Class>elements, restoring visual customizations viaont:iconandont:color - Processes
<DatatypeProperty>elements to rebuildPropertyobjects, respectingont:propertyType,ont:isIdentifier,ont:unit, andont:enumValues - Handles
<ObjectProperty>elements, resolving entity references through explicit annotations orrdf:domain/rdf:rangeURIs - Reassembles relationship attributes using
ont:relationshipAttributeOfto map them back to their parent relationships
Round-Trip Verification
The test suite in src/lib/rdf/roundtrip.test.ts validates that no information is lost during conversion. The test serializes a sample ontology to RDF/XML, parses it back, and asserts deep equality between the original and reconstructed objects.
import { serializeToRDF } from './serializer';
import { parseRDF } from './parser';
import { sampleOntology } from '../../data/sample';
const rdf = serializeToRDF(sampleOntology);
const { ontology: roundTripped } = parseRDF(rdf);
// Jest assertion used in roundtrip.test.ts
expect(roundTripped).toStrictEqual(sampleOntology);
This verification ensures that custom annotations, identifiers, enum values, and data bindings survive the export/import cycle intact.
Practical Code Examples
Serializing an Ontology to RDF
import { serializeToRDF } from './src/lib/rdf/serializer';
import type { Ontology } from './src/data/ontology';
const myOntology: Ontology = {
name: 'Coffee Shop',
description: 'A simple coffee-shop model',
entityTypes: [
{
id: 'customer',
name: 'Customer',
icon: '👤',
color: '#ff6600',
properties: [{
name: 'email',
type: 'string',
isIdentifier: true
}],
},
],
relationships: [],
};
const rdf = serializeToRDF(myOntology);
// rdf contains an OWL/RDF/XML document with ont:isIdentifier preserved
Parsing RDF Back to the Model
import { parseRDF } from './src/lib/rdf/parser';
const { ontology, bindings } = parseRDF(rdfString);
// ontology matches the original structure with full type fidelity
// bindings contains any DataBinding definitions from ont:DataBinding elements
Handling Enum Types
// The parser automatically restores enum values from ont:enumValues annotations
const property = {
name: 'status',
type: 'enum',
enumValues: ['active', 'inactive'],
};
// When serialized, this stores:
// <ont:propertyType>enum</ont:propertyType>
// <ont:enumValues>["active","inactive"]</ont:enumValues>
Summary
- Lossless conversion relies on custom annotations (
ont:propertyType,ont:isIdentifier,ont:enumValues) stored alongside standard OWL properties - Deterministic URIs via
deriveBaseUriensure reliable resource mapping between serializer and parser - Relationship attributes are preserved through
ont:relationshipAttributeOfand reassembled during parsing - Data bindings survive round-trips via dedicated
ont:DataBindingelements - Validation occurs in
src/lib/rdf/roundtrip.test.tsusing deep equality assertions on reconstructed ontologies
Frequently Asked Questions
How does the parser handle custom property types without standard XSD equivalents?
The parser prioritizes the ont:propertyType annotation over XSD datatype inference. It reads this custom element first to restore the original TypeScript type; only if the annotation is missing does it fall back to the XSD_TO_TYPE mapping. This ensures that custom types like internal enums or specialized identifiers are reconstructed accurately even when no direct XSD equivalent exists.
What happens to relationship attributes during round-trip conversion?
Relationship attributes are emitted as separate owl:DatatypeProperty elements marked with ont:relationshipAttributeOf during serialization. The parser detects these markers and groups the properties back into the parent Relationship.attributes array, using ont:fromEntityId and ont:toEntityId to ensure they attach to the correct relationship endpoints.
Why does the serializer use a deterministic base URI?
The deriveBaseUri function generates a stable namespace from the ontology name, creating URIs like http://example.org/ontology/<slug>/. This determinism ensures that when the parser extracts localNameFromUri, it can reliably map RDF resources back to the original entity IDs without requiring additional lookup tables or UUID management.
How is data-binding information preserved in the RDF output?
Data bindings are serialized as ont:DataBinding elements containing attributes for source, table, and serialized columnMapping data. The parser collects all such elements during the XML walk and reconstructs the DataBinding[] array, enabling external data-source configurations to survive export and re-import operations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →