# Customize BPMN Import Customize BPMN import when a source model uses conventions that need a different mapping to native Workflow Engine [schemes](https://docs.workflowengine.io/get-started/concepts/scheme/). `BpmnConverterBuilder` replaces individual conversion visitors, applies `BpmnConverterSettings`, and produces the converter passed to the [BPMN Plugin](https://docs.workflowengine.io/plugins/bpmn-plugin/). The remaining visitors keep their default behavior. ## Choose the replacement scope `BpmnConverterBuilder.Create()` starts a converter builder. Call the required replacement methods and finish with `Build(settings)`; omitting a replacement selects that visitor's standard implementation. Each replacement method returns the same builder, and a later call for the same visitor replaces the earlier choice. Node visitors create [activities](https://docs.workflowengine.io/get-started/concepts/activity/). Flow visitors create outgoing [transitions](https://docs.workflowengine.io/get-started/concepts/transition/) and update their incoming settings. `ProcessVisitor` coordinates complete process conversion, including nested processes and diagram coordinates. | Visitor class | Builder method | Conversion responsibility | | --- | --- | --- | | `TaskNodeVisitor` | `WithCustomTaskNodeVisitor` | Task nodes become activities. | | `GatewayNodeVisitor` | `WithCustomGatewayNodeVisitor` | Gateway nodes become activities. | | `EventNodeVisitor` | `WithCustomEventNodeVisitor` | Event nodes become activities or deferred trigger mappings. | | `SubprocessNodeVisitor` | `WithCustomSubprocessNodeVisitor` | Nonempty subprocess nodes become Inline activities; empty ones become ordinary activities. | | `CallActivityNodeVisitor` | `WithCustomCallActivityNodeVisitor` | Call activity nodes become activities. | | `TaskFlowVisitor` | `WithCustomTaskFlowVisitor` | Task sequence flows become transitions. | | `GatewayFlowVisitor` | `WithCustomGatewayFlowVisitor` | Gateway flows receive branch, trigger, and merge settings. | | `EventFlowVisitor` | `WithCustomEventFlowVisitor` | Event flows receive command, timer, or automatic triggers. | | `SubprocessFlowVisitor` | `WithCustomSubprocessFlowVisitor` | Flows connect converted subprocess activities. | | `CallActivityFlowVisitor` | `WithCustomCallActivityFlowVisitor` | Flows connect converted call activities. | | `ProcessVisitor` | `WithCustomProcessVisitor` | Complete process, subprocess, and diagram conversion. | The task, gateway, and event visitors expose non-virtual dispatch methods such as `AddTask` and `AddOutgoingFlow`. Override their specialized `protected virtual` methods, such as `AddServiceTask` or `AddOutgoingFlowExclusiveGateway`, to change a particular BPMN type. `CallActivityNodeVisitor.AddCallActivity` and `SubprocessNodeVisitor.AddSubprocessActivity` are public virtual methods. The default `ProcessVisitor` first creates nodes, then adds all sequence flows, then updates incoming flows. Incoming updates can override settings assigned during flow creation. Replace `ProcessVisitor` only when changes to individual node and flow visitors cannot express the required conversion. ## Configure conversion settings `Build(BpmnConverterSettings? settings = null)` assigns settings to the converter's visitors. When settings are omitted, visitors use the defaults below. Both members are init-only properties, so choose their values while constructing the settings object. | Property | Type | Default | When read and effect | | --- | --- | --- | --- | | `DateTimeKind` | `DateTimeKind` | `Local` | During fixed `timeDate` conversion, selects XML date handling as `Local`, `Utc`, or `Unspecified`. Duration and expression timers do not use this setting. | | `DefaultAutoSchemeUpdate` | `bool` | `true` | During `BaseNodeVisitor.CreateActivity`, enables or disables automatic scheme updates on the created activity. A custom visitor that bypasses this helper must apply its own defaults. | Converter settings affect subsequent imports through that converter. Creating another converter does not rewrite stored schemes or change existing process instances. The automatic update flag controls an activity's eligibility for the runtime's scheme update mechanism; it does not synchronize a scheme with its original BPMN file. ## Replace call activity conversion The default `CallActivityNodeVisitor` creates an ordinary activity and records an unsupported-call warning. The following configuration replaces that mapping with an application convention: a call to `ReviewOrder` becomes an activity implementation named `Call_ReviewOrder`. `Configure` is an application helper in this example. Pass it an already configured [Workflow Runtime](https://docs.workflowengine.io/get-started/concepts/workflow-runtime/) before starting the runtime. It constructs the custom converter, passes it to `BpmnPlugin`, and registers the plugin with `WithPlugin`. ```csharp filename="BpmnImportConfiguration.cs" using System; using OptimaJet.Workflow.BpmnPlugin; using OptimaJet.Workflow.BpmnPlugin.Import; using OptimaJet.Workflow.BpmnPlugin.Model; using OptimaJet.Workflow.Core.Model.Builder; using OptimaJet.Workflow.Core.Runtime; public static class BpmnImportConfiguration { public static WorkflowRuntime Configure(WorkflowRuntime runtime) { var settings = new BpmnConverterSettings { DateTimeKind = DateTimeKind.Utc, DefaultAutoSchemeUpdate = false }; BpmnConverter converter = BpmnConverterBuilder.Create() .WithCustomCallActivityNodeVisitor(new ApplicationCallVisitor()) .Build(settings); return runtime.WithPlugin(new BpmnPlugin(converter)); } } public sealed class ApplicationCallVisitor : CallActivityNodeVisitor { public override void AddCallActivity( TCallActivity callActivity, BpmnConverterContext context) { IActivityBuilder activityBuilder = CreateActivity(callActivity, context); string calledName = callActivity.CalledElement?.Name; if (string.IsNullOrWhiteSpace(calledName)) { string issue = $"Call activity '{callActivity.Id}' has no calledElement."; context.LogIssue(issue); activityBuilder.CreateCommentary(issue); return; } string actionName = $"Call_{calledName}"; activityBuilder.CreateImplementationAtEnd(actionName); activityBuilder.CreateCommentary($"Application action: {actionName}."); } } ``` `CreateActivity` preserves the standard ID-to-activity-name mapping, the BPMN name-to-state mapping, state-change eligibility for named nodes, and `DefaultAutoSchemeUpdate`. The normal diagram pass still assigns coordinates using the BPMN element ID. The default call activity flow visitor continues to connect the activity to its neighbors. The helper calls `State(flowNode.Name).EnableSetState()` when the BPMN name is not blank and `NotState()` otherwise. These are the same state-setting decisions that an application visitor would apply when creating the activity directly through `context.Builder`. This custom visitor creates an [action](https://docs.workflowengine.io/get-started/concepts/action/) reference; the application must implement and register each `Call_` action before executing the scheme. The example does not start a called BPMN process automatically or reproduce BPMN call activity execution semantics. If the application needs another execution model, change both its action implementation and the conversion convention. ## Inspect conversion before saving `BpmnConverter.ConvertBpmnDocument` converts BPMN XML without saving schemes or starting processes. Use it to inspect conversion issues before allowing an application import. This standalone .NET 10 program reads a BPMN file supplied as its only command-line argument and reports each converted scheme. ```csharp filename="InspectBpmnImport.cs" using System; using System.IO; using OptimaJet.Workflow.BpmnPlugin.Import; if (args.Length != 1) { throw new ArgumentException("Pass the path to one BPMN XML file."); } BpmnConverter converter = BpmnConverterBuilder.Create().Build(); using var reader = File.OpenText(args[0]); foreach (ConvertedProcess convertedProcess in converter.ConvertBpmnDocument(reader)) { Console.WriteLine($"Scheme: {convertedProcess.ProcessDefinition.Name}"); Console.WriteLine($"Skip saving: {convertedProcess.DontSave}"); foreach (string issue in convertedProcess.Issues) { Console.WriteLine($"Issue: {issue}"); } if (convertedProcess.Hint is not null) { Console.WriteLine($"Hint: {convertedProcess.Hint.Message}"); } } ``` The reader overload accepts a `TextReader` and an optional `mainSchemeCode`. The model overload accepts `OptimaJet.Workflow.BpmnPlugin.Model.TDefinitions` and the same optional code. Both return `List`; the model overload uses an already deserialized BPMN document. A nonempty `mainSchemeCode` renames the first converted process that is neither inline nor marked `DontSave`. | Result member | Meaning | | --- | --- | | `ProcessDefinition` | Converted native scheme definition, including activities and transitions. | | `Issues` | Immutable list of conversion warnings gathered for that process. | | `HasIssues` | `true` when `Issues` is nonempty. | | `Hint` | Optional conversion hint with a type and explanatory message. | | `DontSave` | `true` when the hint type is `ConvertedProcessHintType.DontSave`. | The default converter marks a top-level process without activities as `DontSave`. Conversion does not run the runtime's full scheme save and build pipeline, so an issue-free conversion is not proof that a scheme can be saved or executed. XML deserialization and conversion can also throw exceptions; a service integrating this API must handle those failures separately from `Issues`. `BpmnConverterContext` supplies a visitor's `Builder`, immutable `FlowElements`, and `BpmnDocument`. Use `FindMessage`, `GetFlowNode`, and `GetSequenceFlow` to resolve elements; each lookup can return `null`. Call `LogIssue` to add a process-level diagnostic. Add a commentary through the activity or transition builder as well when the warning should remain visible on the corresponding scheme element. ## Related import contracts The [BPMN import guide](https://docs.workflowengine.io/plugins/bpmn-plugin/import-bpmn-diagrams/) applies converters through the Designer's save pipeline. The [supported elements reference](https://docs.workflowengine.io/plugins/bpmn-plugin/supported-elements/) describes the default mapping that a replacement visitor changes. The [BpmnConverter API reference](https://api.workflowengine.io/bpmn-plugin/OptimaJet.Workflow.BpmnPlugin.Import/BpmnConverter/) lists the public conversion overloads. ## Frequently asked questions **How do I customize only one BPMN element type?** Replace the relevant visitor through `BpmnConverterBuilder` and override its specialized virtual method. The builder supplies default visitors for every category you leave unchanged. **Can I inspect a BPMN import without writing to the database?** Yes. `ConvertBpmnDocument` returns converted definitions and their diagnostics without invoking persistence. The Designer upload pipeline adds saving and build validation afterward. **Does changing converter settings update existing schemes?** No. `BpmnConverterSettings` controls conversion of new input; existing definitions retain their current values. Reimporting with overwrite enabled can replace stored schemes through the Designer import pipeline. **Does a custom CallActivityNodeVisitor automatically execute the called BPMN process?** No. A custom visitor defines the native activity mapping, and the application supplies the corresponding execution behavior. The example creates action references that require registered application implementations.