---
title: "Parsing BPMN — SpiffWorkflow 3.0.0 documentation"
description: "The example application assumes that a BpmnProcessSpec will be generated for each process independently of starting a workflow and that these will be immediately serialized and provided with a ID. We’ll discuss…"
source_url: https://spiff.works/docs/spiffworkflow/bpmn/parsing.html
---

☰ Menu

# Parsing BPMN[](#parsing-bpmn "Link to this heading")

The example application assumes that a `BpmnProcessSpec` will be generated for each process independently of
starting a workflow and that these will be immediately serialized and provided with a ID. We’ll discuss serialization
in greater detail later; for now we’ll simply note that the file serializer simply writes a JSON representation of the
spec to a file and uses the filename as the ID.

Note

This is design choice – it would be possible to re-parse the specs each time a process was run.

# Default Parsers[](#default-parsers "Link to this heading")

## Importing[](#importing "Link to this heading")

Each of the BPMN modules (`bpmn`, `spiff`, or `camunda`) has a parser that is preconfigured with
the specs in that module (if a particular TaskSpec is not implemented in the module, `bpmn` TaskSpec is used).

- `bpmn`: `from SpiffWorkflow.bpmn.parser import BpmnParser`
- `dmn`: `from SpiffWorkflow.dmn.parser import BpmnDmnParser`
- `spiff`: `from SpiffWorkflow.spiff.parser import SpiffBpmnParser`
- `camunda`: `from SpiffWorkflow.camunda.parser import CamundaParser`

Note

The default parser cannot parse DMN files. The `BpmnDmnParser` extends the default parser to add that
capability. Both the `spiff` and `camunda` parsers inherit from `BpmnDmnParser`.

Instantiation of a parser has no required arguments, but there are several optional parameters.

## Validation[](#validation "Link to this heading")

The `SpiffWorkflow.bpmn.parser` module also contains a `BpmnValidator`.

The default validator validates against the BPMN 2.0 spec. It is possible to import additional specifications (e.g.
for custom extensions) as well.

By default the parser does not validate, but if a validator is passed in, it will be used on any files added to the parser.

```
from SpiffWorkflow.bpmn.parser import BpmnParser, BpmnValidator
parser = BpmnParser(validator=BpmnValidator())
```

## Spec Descriptions[](#spec-descriptions "Link to this heading")

A default set of `decription` attributes for each Task Spec. The description is intended to be a user-friendly
representation of the task type. It is a mapping of XML tag to string.

The default set of descriptions can be found in `SpiffWorkflow.bpmn.parser.spec_descriptions`.

## Creating a BpmnProcessSpec from BPMN Process[](#creating-a-bpmnprocessspec-from-bpmn-process "Link to this heading")

From the `add_spec` method of our BPMN engine ([engine/engine.py](https://github.com/sartography/spiff-example-cli/tree/main/spiff_example/engine/engine.py)):

```
def add_spec(self, process_id, bpmn_files, dmn_files):
    self.add_files(bpmn_files, dmn_files)
    try:
        spec = self.parser.get_spec(process_id)
        dependencies = self.parser.get_subprocess_specs(process_id)
    except ValidationException as exc:
        self.parser.process_parsers = {}
        raise exc
    spec_id = self.serializer.create_workflow_spec(spec, dependencies)
    logger.info(f'Added {process_id} with id {spec_id}')
    return spec_id

def add_files(self, bpmn_files, dmn_files):
    self.parser.add_bpmn_files(bpmn_files)
    if dmn_files is not None:
        self.parser.add_dmn_files(dmn_files)
```

The first step is adding BPMN and DMN files to the parser using the `add_bpmn_files` and
`add_dmn_files` methods.

We use the `get_spec` to parse the BPMN process with the provided `process_id` (*not* the process name).

Note

Ths parser was designed to load one set of files and parse a process and will raise a `ValidationException`
if any duplicate iDs are present. The available processes are immediately added to `process_parsers`, so
re-adding a file will generate an exception. Therefore, if we run into a problem (the specific case here) or wish
to reuse the same parser, we need to clear this attribute.

### Other Methods for Adding Files[](#other-methods-for-adding-files "Link to this heading")

- `add_bpmn_files_by_glob`: Loads files from a glob instead of a list.
- `add_bpmn_file`: Adds one file rather than a list.
- `load_bpmn_str`: Loads and parses XML from a string.
- `load_bpmn_io`: Loads and parses XML from an object implementing the IO interface.
- `load_bpmn_xml`: Parses BPMN from an `lxml` parsed tree.

## Handling Subprocesses and Call Activities[](#handling-subprocesses-and-call-activities "Link to this heading")

Internally, Call Activities and Subprocesses (as well as Transactional Subprocesses) are all treated as separate
specifications. This is to prevent a single specification from becoming too large, especially in the case where the
same process spec will be called more than once.

The `get_subprocess_specs` method takes a process ID and recursively searches for Call Activities, Subprocesses,
etc used by or defined in the provided BPMN files. It returns a mapping of process ID to parsed specification.

### Other Methods for Finding Dependencies[](#other-methods-for-finding-dependencies "Link to this heading")

- `find_all_specs`: Returns a mapping of name -> `BpmnWorkflowSpec` for all processes in all files that have been
  provided to the parser at that point.
- `get_process_dependencies`: Returns a list of process IDs referenced by the provided process ID
- `get_dmn_dependencies`: Returns a list of DMN IDs referenced by the provided process ID

## Creating a BpmnProcessSpec from a BPMN Collaboration[](#creating-a-bpmnprocessspec-from-a-bpmn-collaboration "Link to this heading")

The parser can also generate a workflow spec based on a collaboration:

```
def add_collaboration(self, collaboration_id, bpmn_files, dmn_files=None):
    self.add_files(bpmn_files, dmn_files)
    try:
        spec, dependencies = self.parser.get_collaboration(collaboration_id)
    except ValidationException as exc:
        self.parser.process_parsers = {}
        raise exc
```

A spec is created for each of the processes in the collaboration, and each of these processes is wrapped inside a
subworkflow. This means that a spec created this way will *always* require subprocess specs, and this method
returns the generated spec (which doesn’t directly correspond to anything in the BPMN file) as well as the processes
present in the file, and theit dependencies.
