Skip to content

BPMN Converter

Converts BPMN 2.0 XML to RDF using the OMG CMOF metamodel for type resolution.

Ontology

Types align to the published BPMN 2.0.2 Ontology (bpmn: prefix).

Type derivation is automatic from CMOF — no configuration needed for full output:

  • <bpmn:userTask> → CMOF class UserTask → bpmn:UserTask
  • <bpmn:sequenceFlow> → CMOF class SequenceFlow → bpmn:SequenceFlow
  • Relationship detection via superclass analysis (SequenceFlow, MessageFlow, Association, etc.)

The implicit end of a data association

A data association is the one relationship BPMN does not write both ends of. It states one as a child element and leaves the other to containment:

<bpmn:userTask id="UserTask_1" name="Capture order">
  <bpmn:dataOutputAssociation id="DataOutputAssociation_1">
    <bpmn:targetRef>DataObjectReference_1</bpmn:targetRef>   <!-- stated -->
  </bpmn:dataOutputAssociation>                              <!-- source: UserTask_1 -->
</bpmn:userTask>

The enclosing activity is therefore taken as the missing end — the source of a dataOutputAssociation, the target of a dataInputAssociation:

<…/relationship/DataOutputAssociation_1> a bpmn:DataOutputAssociation , arch:QualifiedRelationship ;
    arch:source <…/element/UserTask_1> ;
    arch:target <…/element/DataObjectReference_1> .

This is inferred only when the file is silent. The specification permits sourceRef on a dataOutputAssociation, pointing at one of the activity's dataOutputs, and an end stated explicitly is more precise than the activity — so it is left as written. Adding to it would give the relationship two sources and break core-shapes#QualifiedRelationshipShape from the other side.

A plain <bpmn:association> is unaffected: it carries no direction, so there is no implicit end to infer.

References are checked against the metamodel

The CMOF declares the type of every cross-reference — Lane::flowNodeRefs must be a FlowNode, DataAssociation::targetRef an ItemAwareElement — and the XML schema does not enforce it. Both ways a file can break that are reported, and they are treated differently:

The reference What happens
resolves to the declared type kept, nothing said
resolves to something else kept and reported — it resolves, and the author may have meant it
resolves to nothing in the file dropped and reported
[WARN] <Lane_1> flowNodeRefs points at <DataObjectReference_1>, which is a DataObjectReference
rather than a FlowNode. …
[WARN] <Lane_1> flowNodeRefs points at 'Task_Absent', which is not in this file. The reference was
dropped …

The drop is the part worth knowing. Every IRI in the notation layer is minted as {base}#{id} and re-minted into the model namespace from a table of the document's subjects, so a target that is never a subject was not in that table and its raw {base}#{id} reached the output — an edge pointing into a namespace the model does not own. Dropping it keeps the graph sound.

Neither case fails the run: both are defects in the file rather than the conversion, and a usable graph can still be written.

The endpoint inferred for a data association above is exempt, because it is not a claim the file made. It points sourceRef at the enclosing activity where the CMOF declares ItemAwareElement — a knowing departure, since the activity is the only endpoint available when a file declares no ioSpecification.

The label of a shape or an edge is a label node

BPMN records where a label is drawn separately from where its owner is drawn, because a tool lets an author drag the text off the shape. BPMNShape-label and BPMNEdge-label carry that, and both are composite properties — so a BPMNLabel sits one level below the plane's planeElement list.

The DI walk enumerated only that list, so a label was never entered into the remap table, and three things followed from the one omission:

Before Now
Address {base}#Label_Start — outside the model namespace …/view/{viewId}/node/Label_Start
View absent archvis:view → the enclosing view
Type archvis:ArchNode archvis:LabelNode

archvis:LabelNode is the class for a node that positions text rather than depicting something. archvis:ArchNode is the domain of archvis:archElement, so typing a label that way asserted it depicted a model element — and it never carried the archvis:archElement that would have made the assertion true.

<…/view/BPMNDiagram_1/node/Shape_Start> a archvis:ArchNode ;
    archvis:archElement <…/element/StartEvent_1> ;
    archvis:label       <…/view/BPMNDiagram_1/node/Label_Start> .

<…/view/BPMNDiagram_1/node/Label_Start> a archvis:LabelNode , bpmndi:BPMNLabel ;
    archvis:view     <…/view/BPMNDiagram_1> ;
    archvis:bounds-x 82.0 ; archvis:bounds-y 140.0 .

The node is kept rather than dropped because its bounds are not derivable from the shape's: the label above sits below and wider than the 36×36 circle it belongs to, and nothing else records that.

An edge's label carries no archvis:label back-link. archvis:label is declared rdfs:domain arch-vis:Node, and core-vis makes Link a sibling of Node under DiagElement rather than a subclass — so asserting it on an edge would entail, through that domain axiom, that every labelled BPMN sequence flow is a Node. An edge label is reachable through the notation-native bpmndi:label instead, which is emitted for both carriers. An aligned route needs the domain widened to DiagElement upstream.

Worth noting how this was found: the graph conformed to the published shapes both before and after. core-vis publishes no SHACL shapes, so nothing validated the diagram layer at all. The only thing that caught it was the converter's own self-check, reporting 447 incomplete nodes across 20 diagrams in the words it asks you to send upstream.

BPMN Lite (simplified EA-level output)

Pass --type-mapping config/type-mapping-bpmn-lite.yml to map types to the BPMN Lite Ontology (~28 classes vs ~144):

java -jar bpmn2linkedarchi.jar convert process.bpmn \
  --type-mapping config/type-mapping-bpmn-lite.yml \
  --base-iri https://example.org/la/ --format TRIG -o out.trig

Usage

# Full BPMN ontology → TriG
java -jar bpmn2linkedarchi.jar convert \
  process.bpmn \
  --base-iri https://example.org/la/ \
  --model-id demo \
  --format TRIG \
  --include-di \
  -o out.trig

# Full BPMN ontology → Turtle
java -jar bpmn2linkedarchi.jar convert \
  process.bpmn \
  --base-iri https://example.org/la/ \
  --model-id demo \
  --format TURTLE \
  --include-di \
  -o out.ttl

CLI options

Option Default Description
<inputs> required BPMN 2.0 XML file(s)
-o, --output required Output artifact, path[:FORMAT[:PROFILE]]. Repeatable — several artifacts are written from one conversion, which is faster than re-running the converter and is what makes them share one prov:generatedAtTime. PROFILE is full (default), no-geometry, no-views or no-diagrams; see output serialization
--base-iri required Base IRI for minting all resource IRIs, e.g. https://example.org/la/. An http/https IRI, so the resources it mints dereference
--model-id filename Model identifier for a single input. An index id wins over it, and giving it with several inputs is refused — see one model per --model-id. Unlike an index id, the value is not validated
--format inferred Default format for any --output that names none: TRIG / TURTLE / JSONLD / RDFXML / NTRIPLES / NQUADS. A file extension such as ttl is also understood
--include-di true Parse BPMNDI geometry
--emit-raw-di-geometry false Keep the raw di:bounds / di:waypoint triples and their dc:Bounds / dc:Point nodes. By default those are suppressed and geometry is flattened into archvis:bounds-*. Required if you want to validate with --shapes di
--type-mapping none YAML type overrides (use for BPMN Lite)
--[no-]emit-skos-labels true Mirror bpmn:name to a language-tagged skos:prefLabel, the canonical Linked.Archi label required by bpmnsh:RequiredNameShape. bpmn:name is kept either way — the label is added, never moved. --no-emit-skos-labels leaves the output without labels, which does not conform to the published BPMN shapes
--label-language en BCP-47 tag for skos:prefLabel literals
--emit-skos-notation false Replace bpmn:id → skos:notation
--emit-direct-rel-triples false Emit {src} {pred} {tgt} shortcuts, each bridged back to its flow resource with rdf:reifies. Needs predicates: in a --type-mapping
--emit-bpmn-source-target false Also emit bpmn:sourceRef/bpmn:targetRef
--emit-extension-data false Map foreign children of bpmn:extensionElements onto the elements they annotate, taking each child's own qualified name as the predicate. See Extension data
--ns-global-id none Base IRI for extension rdf:resource values written without a prefix, so a bare id resolves into a cross-model graph. Requires --emit-extension-data
--emit-ontology-imports false Emit owl:imports for the BPMN ontologies in the output
--dual-typing true Emit arch:Element / arch:QualifiedRelationship plus arch:ModelConcept alongside the BPMN types
--diagrams-index none Diagram index for batch processing. When given, only indexed files are converted — in every lifecycle state unless --exclude-states says otherwise — and each one's id becomes its model ID
--diagrams-root index location Root directory that index file paths are resolved against
--exclude-states none Leave index entries in these lifecycle states out of the run. Every state is converted by default, so this is the only option that withholds one. Comma-separated, and it cannot name all five. See Lifecycle states
--include-states none Deprecated and ignored: every state is converted by default. Use --exclude-states
--include-drafts false Deprecated and ignored: drafts are converted by default
--ns-core https://meta.linked.archi/core# Override the core namespace
--ns-core-vis https://meta.linked.archi/core-vis# Override the core-vis namespace

Extension data

A BPMN file can carry data the BPMN metamodel knows nothing about — a link to an ArchiMate element, a LeanIX factsheet id, an owning team — as foreign-namespace children of bpmn:extensionElements. --emit-extension-data maps that data onto the element it annotates.

This is BPMN's in-file route. A diagram index can make the same statements without touching the source file, which suits a team that owns the pipeline rather than the models; both routes apply in the same run. Extension data covers the choice and what the routes share.

The index is also the only route for a statement about the diagram or the model as a whole — which architecture state it depicts, which viewpoint it conforms to. bpmn:extensionElements nests inside the element it annotates, so a .bpmn has nowhere to put one:

prefixes:
  arch: https://meta.linked.archi/core#
model:
  id: order-domain
  links:
    arch:architectureState: arch:Baseline     # about the model
views:
  - id: order-fulfillment
    file: order-fulfillment.bpmn
    links:
      arch:architectureState: arch:Target     # about this view

See statements about a view or a model. A view-level block needs the entry to name a view, so it requires the model/views schema rather than the legacy flat one; in the flat schema an entry is a model, and links: there is model-level.

architectureState: is the checked form of one such statement — baseline, target or transitional, with a typo failing the run rather than emitting a link to a term nothing defines. It is index-only for BPMN, for the same reason, and is not gated by --emit-extension-data:

model:
  id: order-domain
  architectureState: baseline
views:
  - id: order-fulfillment
    file: order-fulfillment.bpmn
    architectureState: target

See Architecture state, including why it is orthogonal to status:.

It is off by default. A file authored in a modelling tool is usually full of that tool's own extension elements (Camunda form data, execution listeners), and mapping those unasked would add triples to every existing model.

The element name is the predicate

Write the term you mean as the element name:

<bpmn:extensionElements>
  <am:realizes rdf:resource="kg:CAP-MasterDataManagement"/>
  <x:costCentre>CC-4711</x:costCentre>
</bpmn:extensionElements>

There is nothing to configure, because XML already did the work. Prefixes in element names are resolved by any XML parser — it is the one place they are — so <am:realizes> arrives carrying https://meta.linked.archi/archimate3/onto# from the document's own xmlns:am. The predicate is that namespace plus the local name, and no option, attribute or carrier vocabulary is involved.

What still needs resolving is the object, because it lives in an attribute value where XML will not help. That is the only place this converter does prefix resolution of its own, and the only reason --ns-global-id exists.

Whether an element becomes a link or a literal follows from its object, not from its name:

Content Becomes
rdf:resource="…" a link — the RDF/XML convention, saying "this is a reference" outright
text naming a resource anyway — a declared CURIE, an absolute IRI a link
any other text one literal-valued predicate
element children, or attributes a node carrying its own predicates
nothing at all skipped — no value to record

Use a published ontology for the name. The examples below use Linked.Archi core (arch:) and the ArchiMate 3 ontology (am:) alongside owl: and skos:. A consumer is free to point at their own ontology and every rule here applies unchanged, but a term from a published vocabulary is one a downstream consumer already understands.

A predicate namespace has to end in # or /

An XML namespace is under no such obligation, so xmlns:t="http://example.org/schema/tags" with <t:owner> would mint http://example.org/schema/tagsowner — an IRI that looks plausible and denotes nothing anyone intended. The element is skipped and reported rather than emitted.

Terms worth knowing before inventing one:

To say Use
this process realizes a capability am:realizes
this application serves the task am:serves (with direction="Backward")
this task adds detail to a coarser concept arch:refines
this task is part of a larger concept arch:partOf
who owns the concept arch:conceptOwner (see validation)
where its master data lives arch:masterDataSource
this task and that node describe the same system skos:exactMatch
they correspond, but not exactly skos:closeMatch, skos:relatedMatch
they are one individual, and their properties should merge owl:sameAs — read the caution below first

skos:exactMatch is the default for correspondence, not owl:sameAs

The two say different things and a reasoner treats them differently.

skos:exactMatch says the BPMN element and the other node describe the same real-world thing, without claiming they are one RDF resource. Each keeps its own types and properties, nothing is inferred onto the other, and the link is revocable without touching either source. This is the normal case: a task in a process model is this model's mention of something that has its own, separately maintained description elsewhere.

owl:sameAs asserts the two IRIs denote one resource. A reasoner is entitled to merge everything known about both, in both directions. That is the right tool only when the two IRIs really are alternate names for one node — reconciling an id after a rename, say — and a wrong owl:sameAs silently corrupts every query downstream of it.

Choose with the substitutability test: could a reasoner safely replace one IRI with the other everywhere, forever? If yes, they are one entity → owl:sameAs. Otherwise → skos:exactMatch. Most cross-model links fail the test, because notations describe the same system at different granularities and for different concerns. Full rationale in Extension data → Stating a correspondence.

These relate instances — this task, that component. Relating one language's types to another's (bpmn:UserTask to am:BusinessProcess) is a different job that does not belong in a model file at all; it is authored once per language pair in a cross-language mapping document. See Extension data → Instance-level and type-level.

<bpmn:definitions
    xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL"
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:arch="https://meta.linked.archi/core#"
    xmlns:am="https://meta.linked.archi/archimate3/onto#"
    xmlns:kg="https://example.org/graph/"
    xmlns:lx="https://leanix.example.com/factsheet/">
  <bpmn:process id="Process_GeoMapping" name="Geo Mapping">
    <bpmn:extensionElements>
      <am:realizes rdf:resource="kg:CAP-MasterDataManagement"/>
    </bpmn:extensionElements>
    <bpmn:serviceTask id="Activity_MapGeo" name="Map source geo to target geo">
      <bpmn:extensionElements>
        <am:serves rdf:resource="lx:APP-refdata-importer" direction="Backward"/>
        <arch:refines>archi:BusinessProcess-GeoMapping</arch:refines>
      </bpmn:extensionElements>
    </bpmn:serviceTask>
java -jar bpmn2linkedarchi.jar convert geo-mapping-linked.bpmn \
  --base-iri https://example.org/la/ --model-id geo-mapping \
  --emit-extension-data \
  --format TRIG -o out.trig
<…/bpmn/geo-mapping/element/Process_GeoMapping>
    am:realizes <https://example.org/graph/CAP-MasterDataManagement> .

<https://leanix.example.com/factsheet/APP-refdata-importer>
    am:serves <…/bpmn/geo-mapping/element/Activity_MapGeo> .

The link is flattened into the triple a query would otherwise have to reconstruct. The third element shows rdf:resource being optional where the text already names a resource: archi: is declared, so <arch:refines>archi:BusinessProcess-GeoMapping</arch:refines> is read as a reference rather than a string. Prefer rdf:resource when you want it to be unmistakable.

Note the second triple reads from the application, because am:serves runs that way and the element declared direction="Backward". Without it an author would have to invent an inverse of a term that already exists.

The output above is shown with prefixes for readability. arch: is registered by the converter, but a namespace it does not already know is written out in full — the xmlns declarations in the BPMN file are used for resolution, not carried into the output. Register the ones you link with under namespaces: in a type-mapping file to get short names:

namespaces:
  am: https://meta.linked.archi/archimate3/onto#
  kg: https://example.org/graph/
  lx: https://leanix.example.com/factsheet/

Correspondence and identity need no separate mechanism — an element named skos:exactMatch is a skos:exactMatch triple, and the same goes for owl:sameAs:

<skos:exactMatch rdf:resource="archi:id-abc123-GeoMapping"/>

skos:exactMatch is the right default here, per the caution above: this says the BPMN process and the ArchiMate element describe the same process, which is a correspondence rather than a claim that the two IRIs denote one individual. Write owl:sameAs instead only where the substitutability test passes and a property merge is what you want.

That the predicate is just an element name matters because most cross-model references are typed relations (am:realizes, arch:refines) that neither correspondence nor identity can express. --ns-global-id therefore does something narrower here than in the ArchiMate converter: it supplies a base for bare targets rather than designating an identity field.

How an object is resolved

The predicate needs no help, because it is an element name. The object does: an attribute value is text, and XML resolves no prefixes there, so lx:APP-x in rdf:resource is a plain string until the converter expands it against the document's own xmlns declarations — including one on the annotated element or the extension element itself, following normal XML scoping.

First match wins:

Form Example Result
CURIE with a prefix declared in the document lx:APP-x expanded against the declaration
Absolute IRI https://leanix.example.com/factsheet/APP-x passed through
No prefix, with a base configured CAP-1 + --ns-global-id https://example.org/graph/ https://example.org/graph/CAP-1
Anything else nowhere:CAP-1 warning, value kept as a literal

Write an http/https IRI when you write one in full: it dereferences, so a consumer can follow it. A few schemes without :// are accepted as absolute too — urn:, mailto:, tag:, doi:, data: — because a value like urn:example:x is unambiguously an IRI rather than a CURIE. They are accepted, not recommended.

The document's own declaration wins over reading the value as an absolute IRI, because a CURIE and an IRI have the same prefix:rest shape and a declared prefix is the better evidence. A prefixed value with no declaration is reported rather than emitted as a nowhere:CAP-1 IRI that resolves nowhere:

[WARN] flow.bpmn: <skos:exactMatch> on 'ManualTask_1' points at 'nowhere:CAP-Unknown', which is not
a resolvable IRI — prefix 'nowhere:' in 'nowhere:CAP-Unknown' is not declared in the BPMN document.
Add xmlns:nowhere="…" to bpmn:definitions, or write the IRI in full. Kept as a literal, so it is
not a link in the graph.

The value is kept as a literal so nothing authored is lost, but it is not a link — a query for links will not match it. Treat the warning as an error in a publishing pipeline.

Note the asymmetry with text content: a bare name in rdf:resource resolves against --ns-global-id because the attribute says a reference was intended, while the same bare name as text content stays a literal. <x:tier>gold</x:tier> is a value, not an IRI.

A link points at something in another graph, so nothing in this file says what the target is. Where the term you used constrains its object's class, validating the BPMN output on its own will report a violation that merging the graphs would resolve.

arch:conceptOwner is the one to know about: the core shapes require its object to be an arch:Stakeholder, and a BusinessRole typed in your ArchiMate model carries no type here.

[Violation] arch:conceptOwner must point to a Stakeholder.
    focusNode <…/bpmn/geo-mapping/element/UserTask_Review>

The link itself is correct and the merged graph conforms. Either validate the BPMN output together with the model that types the target, or pick a term whose range is unconstrained (arch:masterDataSource, arch:refines, arch:partOf, am:realizes, am:serves, owl:sameAs and the skos: mapping terms all validate cleanly on their own). There is no way to assert the target's type from the BPMN side, because the subject of a link is always the annotated element.

direction

direction Emitted
absent, Forward, None element predicate target
Backward target predicate element
Both both of the above
anything else warning, read as Forward

None is folded into Forward because a triple has a direction whether or not the author expressed a preference. Where the target could not be resolved to an IRI, only the forward triple is written — a literal cannot be the subject of the inverse — and that is reported too.

Data with no published term

An element whose content is plain text becomes a literal-valued predicate. This is the extension point for facts no published ontology has a term for — a cost centre, an internal service level. Where a standard term does exist, prefer it: arch:masterDataSource says where master data lives far better than a vocab:source string, because a consumer already knows what it means and the value is a resource rather than a name.

A leaf is one predicate; a group keeps its parts together on a node of its own, so two service levels on one element stay distinguishable:

<bpmn:serviceTask id="ServiceTask_1">
  <bpmn:extensionElements>
    <arch:masterDataSource rdf:resource="lx:APP-refdata-importer"/>
    <x:costCentre>CC-4711</x:costCentre>
    <x:serviceLevel>
      <x:tier>gold</x:tier>
      <x:responseTime>PT4H</x:responseTime>
    </x:serviceLevel>
    <x:criticality level="high">tier-1</x:criticality>
  </bpmn:extensionElements>

with xmlns:x="https://example.org/vocab#" declared in the file:

<…/element/ServiceTask_1>
    arch:masterDataSource <https://leanix.example.com/factsheet/APP-refdata-importer> ;
    vocab:costCentre      "CC-4711" ;
    vocab:serviceLevel    [ vocab:tier "gold" ; vocab:responseTime "PT4H" ] ;
    vocab:criticality     [ vocab:level "high" ] .

The predicate namespace comes from the document, so there is no option to pass and no chance of two extension vocabularies colliding: <a:tier> and <b:tier> stay distinct predicates.

Attributes become predicates on the group node, which is why criticality is a node rather than a literal. An unprefixed attribute has no namespace in XML, and level on its own is not a predicate, so it is read against the namespace of the element it qualifies. direction and rdf:resource are never emitted as data — they say how to map the element rather than asserting anything about it.

What is not mapped

Why
dc: / dcterms: children Already read into the provenance graph as the file's title, author and dates. Mapping them again would state one fact under two vocabularies
Children in a BPMN namespace The CMOF parse's business, not this one's
An empty element with no attributes and no text No value to record, and a predicate pointing at an empty string would claim there is one
Extension data on an element with no id Nothing to attach it to, since IRIs are minted from the id. Reported, with the element named
Mixed content — text alongside child elements No predicate to hang the text on
An element whose namespace does not end in # or / The predicate would run into the local name. Reported, with the namespace named
A type + target wrapper element The shape an earlier draft of this feature used. Reported, with the direct form to write instead — see below
bpmn:relationship Out of scope; see below

If you authored against the type + target wrapper

An earlier draft of this feature wrapped the predicate in a carrier element and put it in a type attribute:

<x:link type="am:realizes" target="kg:CAP-1"/>   <!-- not mapped -->

That shape is refused, because mapping it now would take the carrier's own name as the predicate and emit literals called type and target — junk that looks like data. The run says what to write:

[WARN] flow.bpmn: Skipped <x:link type="am:realizes"> on 'Task_1': the predicate is the element
name, not a type attribute. Write it as <am:realizes rdf:resource="kg:CAP-1"/>, declaring a prefix
for 'am' if it has one.

The wrapper was dropped rather than kept alongside the direct form because it is strictly worse: it needed a carrier vocabulary nobody had published, and it moved the predicate into an attribute where XML will not resolve its prefix, so the converter had to re-implement resolution that the XML parser already does for element names.

bpmn:relationship is out of scope

BPMN's own <bpmn:relationship> element looks like the intended mechanism for relating a process to an external artifact, and it is valid BPMN, but it is not mapped. Its source and target children are emitted as empty typed nodes and their content does not reach the graph:

<…/element/Rel_1/source> a bpmn:Source .    # no value

Use extensionElements instead. It attaches the link to the element it describes rather than to a detached definitions-level node, and tools round-trip it.

Worth knowing if you have such a file: bpmn:source and bpmn:target are xsd:QName in the BPMN schema, so an undeclared prefix — tns: is the common one — makes render fail while convert succeeds, because the two use different parsers and only Camunda's resolves the QName. The [WARN] Skipped … line names the prefix and the line:

[WARN] Skipped flow.bpmn: SAXException while parsing input stream — Error: URI=null Line=16:
UndeclaredPrefix: Cannot resolve 'tns:Process_1' as a QName: the prefix 'tns' is not declared.

BPMN ontology packages

All packages are published on meta.linked.archi and fetched at runtime; none are bundled in the JAR.

Package Namespace Asset name Content
BPMN core https://meta.linked.archi/bpmn/onto# bpmn Process, Task, Event, Gateway, Flow
BPMN DI https://meta.linked.archi/bpmn/di# bpmn-di BPMNDiagram, BPMNShape, BPMNEdge
DI core https://meta.linked.archi/bpmn/di-core# bpmn-di-core Abstract Diagram, Shape, Edge
DC https://meta.linked.archi/bpmn/dc# bpmn-dc Bounds, Point, Font
Infrastructure https://meta.linked.archi/bpmn/infra# bpmn-infra Definitions, Import
Suite imports https://meta.linked.archi/bpmn/suite# bpmn-suite owl:imports aggregator only — no axioms of its own, so it is not loaded by default. The mapping to arch: core types lives in BPMN core above
BPMN Lite https://meta.linked.archi/bpmn-lite/onto# — EA-level subset (~28 classes), via --type-mapping

Architecture

BPMN 2.0 XML
  → BpmnXmlToRdfConverter (CMOF-driven StAX parser)
  → Raw RDF model (CMOF-typed, fragment IRIs)
  → LinkedArchiEmitter (IRI remapping + named graph emission)
  → TriG / Turtle output

Render

Renders BPMN files to SVG using the BPMNDI geometry already in the XML (Camunda model library) — no layout engine, so the result matches the authoring tool: event circles, gateway diamonds, task boxes with type icons, pools and lanes, sequence-flow arrows.

java -jar bpmn2linkedarchi.jar render process.bpmn -o svg/

# Batch, hyperlinked to the graph, with custom shape styling
java -jar bpmn2linkedarchi.jar render models/*.bpmn \
  --base-iri https://example.org/la/ \
  --diagrams-root models/ \
  --diagrams-index models/diagram-index.yaml \
  --shape-config config/shape-config-bpmn.yml \
  -o out/svg/
Option Default Description
<inputs> required BPMN 2.0 XML file(s)
-o, --output-dir required Directory the SVG files are written to
--base-iri none When given, every shape is wrapped in <a href> pointing at its element IRI ({base}bpmn/{modelId}/element/{id}), matching what convert mints
--shape-config built-in defaults YAML file with per-type style overrides (fill, stroke, strokeWidth, icon)
--diagrams-index none YAML index selecting which diagrams to render and their model IDs
--diagrams-root index location Root directory that index file paths are resolved against
--exclude-states none Leave index entries in these lifecycle states out of the run. Every state is rendered by default. Comma-separated, and it cannot name all five. Resolved exactly as convert resolves it, so a diagram in the graph has a picture
--include-states none Deprecated and ignored: every state is rendered by default. Use --exclude-states
--include-drafts false Deprecated and ignored: drafts are rendered by default
--[no-]render-archived true Draw entries with status: archived. --no-render-archived leaves them out; convert is unaffected, so the diagram keeps its IRIs but its image URL stops resolving

Output naming: with --diagrams-index the file is <id>.svg and any sub-directory in the index file: path is mirrored under the output directory. Without an index the SVGs are written flat as <filename>.svg. This and the rest of the shared render behaviour are described in SVG Rendering.

The same id names the SVG here and the IRI path segment in convert ({base}bpmn/{id}/…), which is what lets a link in the diagram resolve against the graph. Because it is used unescaped in both, it must be a slug — ^[a-z0-9][a-z0-9._-]*$ — and id and file: must each appear once in the index. A violation fails the command rather than being silently rewritten or dropped; see id rules. Note that changing an id renames the published SVG and moves every element IRI for that model.

Index metadata (title, author, version, created, modified, description) is rendered into an info box at the top of the SVG, overlaid on top of any metadata found in the BPMN file's own documentation and extension elements.

Per-file failures are reported as [WARN] Skipped … and the command exits 1; a failure before rendering starts (bad shape config, missing index) exits 2.

Shape config

shapes:
  UserTask:
    fill: "#ddeeff"
    stroke: "#333333"
    icon: |
      <circle cx="8" cy="5" r="3.5" fill="none" stroke="#555" stroke-width="1.2"/>
      <path d="M1 15 Q8 9 15 15" fill="none" stroke="#555" stroke-width="1.2"/>
  ExclusiveGateway:
    fill: "#ffffcc"
    stroke: "#333333"

Types not listed keep their built-in rendering. icon is a raw SVG snippet drawn in a 16×16 space at the top-left of the shape. A full reference file ships in the distribution as config/shape-config-bpmn.yml.

Ontology download

Downloads the published BPMN ontologies and SHACL shapes from meta.linked.archi into a directory, so they can be inspected or passed back in via --shapes. Nothing is bundled in the JAR — the files come from the registry at runtime.

# Everything (ontologies + shapes)
java -jar bpmn2linkedarchi.jar ontology --dir ontologies/

# Just one category
java -jar bpmn2linkedarchi.jar ontology --dir ontologies/ --only SHACL
Option Default Description
--dir (required) Output directory
--only ALL Download a subset: ALL, ONTOLOGY or SHACL
--overwrite false Overwrite files that already exist
--asset-dir <java.io.tmpdir>/linked-archi-assets Where fetched documents are held
--offline false Use documents already fetched instead of downloading

Existing files are skipped unless --overwrite is given.

Validate

Runs SHACL validation on converted output. Shapes and ontologies are fetched from meta.linked.archi at runtime.

java -jar bpmn2linkedarchi.jar validate -i out.trig

Default shapes are bpmn-shapes, bpmn-infra-shapes and core-shapes, which constrain the normalized output this converter produces: type correctness and single-valuedness of BPMN attributes, the naming rule (bpmnsh:RequiredNameShape), and — from core — that every connector has exactly one arch:source and arch:target. The five BPMN ontologies plus core are loaded for rdfs:subClassOf reasoning, so the alignment to arch:Element / arch:QualifiedRelationship that bpmn/onto declares resolves.

The diagram-interchange shapes are not in the default set, because they constrain raw DI structures (di:bounds pointing at a dc:Bounds node, di:waypoint) and convert flattens geometry into archvis:bounds-* unless told otherwise. The shape set has to match how you converted:

# Raw DI geometry -> validate with the DI shapes too
java -jar bpmn2linkedarchi.jar convert process.bpmn -o out.trig --emit-raw-di-geometry
java -jar bpmn2linkedarchi.jar validate -i out.trig --shapes di

Selecting di (or all) against normalized output reports missing di:bounds and di:waypoint for every shape and edge — that is the shape set being wrong for the data, not a defect in the output. Use the default set for normalized output.

--shapes all selects every published BPMN shape document, and individual names (bpmn-shapes, bpmn-di-shapes, bpmn-di-core-shapes, bpmn-dc-shapes, bpmn-infra-shapes) can be combined freely. Run validate --list-assets to see them.

Naming

BPMN's naming rule is bpmnsh:RequiredNameShape, published in bpmn-shapes. It requires a language-tagged skos:prefLabel on the twelve classes BPMN expects to be named — activities and their task subtypes, processes, collaborations, conversations, pools, lanes, data objects and stores, messages — and deliberately does not target gateways, events or connectors, whose name the spec leaves optional. So unnamed gateways, events and sequence flows are valid and report nothing.

bpmn:name alone does not satisfy it. skos:prefLabel is the canonical Linked.Archi label that every consumer reads, and it must carry a language tag; bpmn:name is the retained source attribute, a plain xsd:string. convert emits both by default, which is what the published converter contract asks for:

<…/element/Task_CheckInventory>
    a              bpmn:ServiceTask ;
    bpmn:name      "Check Inventory" ;        # source attribute, verbatim
    skos:prefLabel "Check Inventory"@en .     # canonical label

Convert with --no-emit-skos-labels and every element that must be named fails this rule. Set --label-language where the source is not English.

There is no core label rule any more: core-shapes#ElementLabelShape and #ViewLabelShape were withdrawn, because requiring a label on every arch:Element is not sound across notations. Nothing needs disabling for BPMN, so this converter no longer passes --without-shape by default. To switch the BPMN rule off for a run:

java -jar bpmn2linkedarchi.jar validate -i out.trig --without-shape required-names

One exception, with --emit-extension-data: a link whose term constrains its object's class is reported when the target is typed in another graph rather than in this file. See Validation and cross-graph links.

Exits 0 when the data conforms, 1 when violations are found, 2 on error.

See Validation for the shared engine, all options, coverage reporting and report format.