# RDF Parser/Serializer Implementation for Round-Trip Support in Ontology-Playground

> Explore the RDF parser/serializer implementation for round-trip support in Ontology-Playground. Learn how custom OWL annotations preserve metadata during RDF/XML conversion.

- Repository: [Microsoft/Ontology-Playground](https://github.com/microsoft/Ontology-Playground)
- Tags: internals
- Published: 2026-07-23

---

**The RDF parser/serializer achieves round-trip support through coupled modules in [`src/lib/rdf/serializer.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/src/lib/rdf/serializer.ts) and [`src/lib/rdf/parser.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/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`](https://github.com/microsoft/Ontology-Playground/blob/main/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`](https://github.com/microsoft/Ontology-Playground/blob/main/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`](https://github.com/microsoft/Ontology-Playground/blob/main/src/lib/rdf/parser.ts)**: Imports RDF/XML by parsing the document with `DOMParser`, reconstructing `EntityType` instances 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:

1. **Stable Base URI Derivation**: The `deriveBaseUri` function 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.

2. **Custom Type Annotations**: While standard RDF uses XSD datatypes, Ontology-Playground stores the original **property type** in `ont:propertyType` and **enum values** in `ont:enumValues`. The parser reads these custom elements first, falling back to `XSD_TO_TYPE` mappings only when annotations are absent.

3. **Identifier Flag Preservation**: The serializer emits `ont:isIdentifier` for primary key properties. The parser detects this annotation and sets the corresponding `isIdentifier` boolean on the reconstructed `Property` object.

4. **Relationship Attribute Reassembly**: Attributes attached to relationships are serialized as separate `owl:DatatypeProperty` elements with the annotation `ont:relationshipAttributeOf`. During parsing, these are grouped back into the `Relationship.attributes` array using the `from` and `to` entity mappings stored in `ont:fromEntityId` and `ont:toEntityId`.

5. **Data Binding Retention**: `ont:DataBinding` elements capture external data-source mappings including `source`, `table`, and `columnMapping`. The parser detects these elements and reconstructs the `DataBinding[]` array alongside the ontology model.

## Implementation Details by Module

### Serializer ([`src/lib/rdf/serializer.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/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:propertyType` to store the original TypeScript type
- `ont:isIdentifier` for primary key flags
- `ont:unit` for measurement units
- `ont:enumValues` for 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`](https://github.com/microsoft/Ontology-Playground/blob/main/src/lib/rdf/parser.ts))

The parser validates the XML structure using `DOMParser`, then walks the OWL elements to reconstruct the model:

- Reads `rdfs:label` and `rdfs:comment` for ontology metadata
- Extracts `EntityType` details from `<Class>` elements, restoring visual customizations via `ont:icon` and `ont:color`
- Processes `<DatatypeProperty>` elements to rebuild `Property` objects, respecting `ont:propertyType`, `ont:isIdentifier`, `ont:unit`, and `ont:enumValues`
- Handles `<ObjectProperty>` elements, resolving entity references through explicit annotations or `rdf:domain`/`rdf:range` URIs
- Reassembles relationship attributes using `ont:relationshipAttributeOf` to map them back to their parent relationships

## Round-Trip Verification

The test suite in [`src/lib/rdf/roundtrip.test.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/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.

```typescript
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

```typescript
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

```typescript
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

```typescript
// 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 `deriveBaseUri` ensure reliable resource mapping between serializer and parser
- **Relationship attributes** are preserved through `ont:relationshipAttributeOf` and reassembled during parsing
- **Data bindings** survive round-trips via dedicated `ont:DataBinding` elements
- **Validation** occurs in [`src/lib/rdf/roundtrip.test.ts`](https://github.com/microsoft/Ontology-Playground/blob/main/src/lib/rdf/roundtrip.test.ts) using 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.