Skip to content

Type Mapping Configuration

All converters support a --type-mapping YAML file to override how source notation types map to ontology IRIs.

Substitution, not alignment

A mapping entry replaces a type; it does not relate two types

A --type-mapping entry replaces the type a converter would otherwise emit. Mapping userTask to myont:HumanActivity makes the output say a myont:HumanActivity instead of a bpmn:UserTask — it does not state any relationship between the two classes, and no skos:exactMatch, skos:closeMatch or owl:equivalentClass triple is produced.

That distinction matters when the substituted type belongs to a vocabulary a consumer also knows by another name. The shipped type-mapping-bpmn-lite.yml is the clear case: it swaps bpmn:UserTask for bpmnl:UserTask, and the fact that those two denote the same concept is asserted in the published BPMN Lite ontology, which carries skos:exactMatch on each of its classes — not by the mapping file and not in the converter output.

So: use --type-mapping to choose which vocabulary the output speaks. To state that one vocabulary's concepts correspond to another's, author a cross-language mapping document instead — see Authoring cross-language mappings. Converters never emit correspondence triples of their own; mappings are editorial judgements, not derivations.

Retyping a relationship carries its predicates with it

Because a relationships: entry replaces the class, it also invalidates the predicates that were declared against the class it replaced. A LeanIX edge retyped from lmm:Requiring to am:Serving no longer answers to lmm:qualifiedRequires, whose rdfs:range names lmm:Requiring.

This is not a cosmetic mismatch. rdfs:range is an entailment, not a constraint, so a reasoner reading {src} lmm:qualifiedRequires {rel} will infer rel a lmm:Requiring — putting back exactly the class the mapping set out to replace, without anything reporting a conflict.

So a converter will not reuse a published predicate once you have retyped the class it belongs to:

  • qualifiedPredicates: named — your predicate is used.
  • not named — the relationship falls back to arch:hasQualifiedRelationship, whose range is arch:QualifiedRelationship and so always holds. The run warns and names the key to add. The relationship is still reachable; it just no longer says of what kind on the way in.
  • predicates: not named — there is no generic core predicate for a shortcut triple, so the direct triple is omitted entirely rather than written with a term that contradicts the new type. Nothing is lost: the relationship resource carries the statement either way.

The practical rule is one predicates: and one qualifiedPredicates: entry per relationships: entry. The vocabulary you are retyping into will normally publish both — ArchiMate declares a qualified form for every relationship class, for instance — so this is naming existing terms rather than inventing them. playground/config/type-mapping-leanix.yml is a worked example.

Which key a lookup matches

elements: keys are the element or fact sheet type name as your source spells it.

relationships:, predicates: and qualifiedPredicates: keys are matched against the relationship name your source declares first — relApplicationToITComponent, sequenceFlow — and against the canonical name the converter resolved from it second, such as Requiring. Prefer the declared name: it is the one visible in your export. All three sections resolve the same way, so a mapping keyed consistently cannot retype a relationship while leaving its predicates behind.

YAML structure

# Declare namespace prefixes for output readability
namespaces:
  ont: https://custom.linked.archi/ont#

# Override vocabulary IRIs (BPMN only)
vocab:
  arch:Element: https://custom.linked.archi/ont#Element

# Override element type IRIs
elements:
  BusinessActor: https://custom.linked.archi/ont#BusinessActor
  userTask:      https://custom.linked.archi/ont#HumanActivity
  class:         https://custom.linked.archi/ont#DomainEntity

# Override relationship type IRIs
relationships:
  Serving:       https://custom.linked.archi/ont#Serving
  sequenceFlow:  https://custom.linked.archi/ont#Triggers

# Direct predicate, for the {src} {pred} {tgt} shortcut under --emit-direct-rel-triples
predicates:
  Serving:       https://custom.linked.archi/ont#serves
  sequenceFlow:  https://custom.linked.archi/ont#triggers

# Qualified predicate, from the source element to the relationship resource. Always
# emitted — arch:hasQualifiedRelationship where you name none. See below.
qualifiedPredicates:
  Serving:       https://custom.linked.archi/ont#qualifiedServes

# View type overrides (ArchiMate only)
views:
  Diagram:       https://custom.linked.archi/ont#ArchView

# Metadata predicate overrides (BPMN only)
metadata-predicates:
  title:         http://purl.org/dc/terms/title

# Property keys whose value is a reference, not a string (ArchiMate only)
object-properties:
  - arch:architectureState
  - ':realizes'

# House spec fields to read as relationships, and the default kind of a bare
# reference in each (Backstage only)
spec-relations:
  deployedTo: Resource

# House spec fields whose value is a literal (Backstage only)
spec-literals:
  - tier

# House metadata fields whose value is a literal (Backstage only)
metadata-literals:
  - costCentre

Every other section maps a type to an IRI. This one is about a value.

A promoted property key normally carries a literal, which is right for a cost centre and wrong for anything naming another resource. Listing a key here says its values are references, so they are resolved to IRIs:

namespaces:
  arch: https://meta.linked.archi/core#

object-properties:
  - arch:architectureState

An ArchiMate property arch:architectureState = arch:Target then reaches the graph as arch:architectureState arch:Target rather than as the string "arch:Target" — which is what lets a model populate a published owl:ObjectProperty instead of forking the term into a local datatype property.

Values resolve as an absolute IRI, a prefixed name against namespaces: (falling back to --ns-vocab), or a bare local name against --ns-vocab. A value that resolves to none of those emits no link: it is kept as a schema:additionalProperty pair and reported, rather than emitted as a literal that would contradict the property's range.

Keys are matched as authored in the model, so a key written :realizes is declared ':realizes' — quoted, because a leading colon starts a YAML alias otherwise.

Read by archimate2linkedarchi today. See ArchiMate → Making a property value a link for the full rules and for why this is declared rather than detected from the ontology.

House fields in a Backstage descriptor

spec-relations:, spec-literals: and metadata-literals: are the same idea one level up: they name keys the Backstage descriptor format does not declare, and say what the value of each one is.

namespaces:
  example: https://vocab.example.org/id/

spec-relations:               # the value names another resource
  deployedTo: Resource        # …and a bare reference defaults to kind Resource

spec-literals:                # the value is a value
  - tier

metadata-literals:            # a house key in metadata rather than in spec
  - costCentre

predicates:
  deployedTo: https://vocab.example.org/arch#deployedTo    # the predicate to write
  tier: https://vocab.example.org/arch#tier
  costCentre: https://vocab.example.org/arch#costCentre

relationships:
  deployedTo: https://vocab.example.org/arch#Deployment    # optional; makes it a qualified edge

The split matters because the two are indistinguishable in the file: deployedTo: prod-cluster and tier: gold are one YAML shape, and only the author knows which names an entity. This is object-properties: above applied to a field rather than a property key, and for the same stated reason — links and literals are declared, not detected.

spec-relations: is a map because a reference needs a default kind, playing the part the descriptor format plays for spec.owner (Group). spec-literals: is a list because a literal needs nothing per key: its datatype comes from the catalog, since the YAML loader has already resolved 3 to an integer and gold to a string, and the emitter maps that onto XSD. Declaring the datatype here would let the configuration disagree with the file.

The kind is only the default segment of an entity reference, not a type declaration, so it is ignored by a value that resolves as an IRI — a prefixed name against namespaces:, or an absolute IRI — which is how a field points at something the catalog does not describe. The entry is still what makes the field readable at all. A kind outside the seven the ontology models is accepted too; give it a class under elements:, keyed on the lowercased kind.

Without an entry in relationships: a relation field is emitted as a direct triple only, since a qualified relationship with no class would be an untyped node — and the Backstage converter will not invent a class in bs:, a namespace it reads and does not own. The section has no effect on a literal field, which is a property rather than an edge.

metadata-literals: is the metadata counterpart of spec-literals:, with identical predicate and datatype handling. It is a separate list rather than a shared one because metadata.tier and spec.tier are two different statements, and one declaration must not silently answer for both. It has no relation counterpart: metadata is where Backstage puts descriptive fields, and a reference belongs in spec or in metadata.labels — the latter being read with no declaration at all.

A declared key a descriptor does not carry is silent. An undeclared key found in a descriptor is reported, as is a key declared in both spec sections, which is contradictory and so emits nothing.

elements: matters here too. A kind outside the seven the ontology models — Template, Location, or one of your own — takes its class from elements: keyed on the lowercased kind, or from --ns-vocab. Nothing is minted in bs:, so with neither the entity is emitted as an arch:Element carrying no notation class, and the run reports it.

None of these sections causes an OWL axiom to be emitted. The predicate is used; whether it is an owl:ObjectProperty or an owl:DatatypeProperty, and what its domain and range are, belongs to the ontology you publish and load alongside the output.

Read by backstage2linkedarchi only. See Backstage → Custom spec fields for value resolution, the two output shapes and what gets reported.

How overrides work

Each converter has a default type resolution strategy:

Converter Default behaviour Override trigger
ArchiMate xsi:type → {nsArchimate}{TypeName} Entry in elements: or relationships:
BPMN CMOF class → {BPMN_BASE}{ClassName} Entry in elements: or relationships:
PlantUML Stereotype → UmlTypeDefaults lookup Entry in elements: or relationships:

When an entry exists in the YAML, it takes precedence over the default. When no entry exists, the default is used.

Example configs

See playground/config/ for working examples:

  • type-mapping-archimate.yml — ArchiMate reference with predicate mappings
  • type-mapping-bpmn-full.yml — BPMN full reference (documents defaults)
  • type-mapping-bpmn-lite.yml — BPMN → BPMN Lite ontology override
  • type-mapping-plantuml.yml — PlantUML → UML reference (documents defaults)
  • type-mapping-plantuml-custom.yml — PlantUML → custom domain ontology