Backwards compatibility for custom properties

I’ve been working with a custom modeler instance and recently upgraded it to BPMN 9 (from BPMN 7, since it hadn’t been updated in a while. I rewrote the implementation of bpmn-js-properties-panel to match the available examples, and confirmed that the new version works in isolation.

Unfortunately, it appears that when bpmn-js-properties-panel changed the way custom properties are stored and accessed, which means there’s no easy way to get at the older information stored in custom properties. We went from this:

<bpmn:task id="Activity_1fz3oh0" name="Task 1">
    <bpmn:documentation>Task 1 doc</bpmn:documentation>
        <mymodeler:property name="nameDE" value="Task 1 de" />
        <mymodeler:property name="elementDocumentationDE" value="Task 1 doc de" />


<bpmn:task id=\"Activity_1fz3oh0\" name=\"Task 1\">
    <bpmn:documentation>Task 1 doc</bpmn:documentation>
    <bpmn:extensionElements />

and when I write new values to the custom properties, they get saved as

  <bpmn:task id="Activity_1fz3oh0" name="Task 1" modelervs:nameDE="test german name">
      <bpmn:documentation>Task 1 doc</bpmn:documentation>
      <bpmn:extensionElements />

Code snippet:

(with bpmn-js v9.3.0 and bpmn-js-properties-panel v1.3.0)

console.log("data in: ");
await modeler.importXML(bpmnXML);  
console.log("result: ");
console.log(await modeler.saveXML());

bpmnXML has extensionElements, whereas the result of modeler.saveXML() has none.

When I import an XML diagram in the first format, I get the second format out. It seems like the extension elements just disappear on import. Is there something obvious I’m missing, or do I have to write a manual data extraction system to work off of the new system? I’ve been digging around in the properties-panel changelog and docs, but I can’t find anything to suggest a possible solution.


The way custom properties are persisted in the XML should not have changed with the newer versions of the properties panel. The moddle descriptors are the same, only the properties panel components structure change, but you can set and get custom properties in the same manner with the new style.

Are you able to share both your old and new implementation in a way we can inspect it? E.g. via CodeSandbox.

I’ve been trying to set up a CodeSandbox, but now I have a brand new error. Here’s the sandbox, but it’s throwing some kind of error in the saveXML(). I don’t have this error in my own code. In the meantime, at least the code is there.
If I figure out how to fix it, I’ll try to make a sandbox for the older version as well. But it’s a bit difficult to demonstrate without saving the XML from the modeler.

Update: I’ve made a sandbox for the older version, here, but it doesn’t work either and I’m driving myself mildly insane. Is there something particular I need to do to get bpmn projects to run on CodeSandbox? I even tried setting one up with the properties-panel-extension example, just to have a point of comparison, but it’s throwing errors as well.

If CodeSandbox isn’t working for you, feel free to share it differently (e.g. via GitHub project).

I can share the code itself, it just doesn’t run. I can’t share the Github project since I’m not the owner and it’s a private project. I’ve clearly done something wrong in the implementation - can you take a look and see if maybe it’s a simple issue?

I’ve taken a look at the CodeSandbox you shared here. It seems like the script is loaded before the DOM is ready; that’s why it throws as the properties panel parent can’t be found.

We have simple templates for the old and new properties panel framework, so maybe you can use these as starting point.

That would solve the test in the properties-panel-extension example, but that’s not actually my code - I don’t need to get that one working. I just need to work out the problems with this sandbox, where the saveXML() is not working. Could you just look at the code in that sandbox and see if there’s anything wrong with it in terms of BPMN.js, please? The other ones were just for comparison, they do not matter.

Edit: I’ve since updated the sandbox and check the element objects instead of saving to XML, so it works now. Could you see why the extension elements disappear after load?

Given the moddle descriptors you shared, the way you set the properties in the properties panel (modeling.updateProperties), I’d exactly expect the XML output you shared (setting the custom properties as attributes).

<bpmn:task id="Activity_1fz3oh0" name="Task 1" modelervs:nameDE="test german name"/>

Are you using the same moddle descriptors in your old version? If you want to have your custom properties as extension element (e.g. via modelervs:property as you suggested), you need to specify that and also set the extension elements accordingly (Example: TaskDefinitionProps).

I don’t quite understand how element factories work but I’ve gotten somewhere similar by following the model extension example. I can now reliably write extension elements, and I’ve changed the moddle descriptor back to what it was before - thank you!

There’s one thing I still can’t figure out. On initial load, shouldn’t the model have its extension elements, even if my code for the properties panel handles them the wrong way? I can’t see why they disappear even in the XML. Can you think of anything? (Otherwise, I’ll just post my workaround and close the question.)


I’d suggest opening another thread for this, as this might not be related to the properties panel but a general import concern.

Sharing your solution would be really appreciated :+1:

Okay, final solution here. (I’m going to update the Code Sandbox as well).


"types": [
      "name": "property",
      "superClass": [
      "properties": [
          "name": "name",
          "type": "String",
          "isAttr": true
          "name": "value",
          "type": "String",
          "isAttr": true

Extract the elements using an XML DOM parser:

const extElemHelper = (xml) => {

        var extensionDict = {};
        var parser = new DOMParser();
        var xmlData = parser.parseFromString(xml, "text/xml");
        var elements = xmlData.getElementsByTagName("bpmn:extensionElements");

        for(var i = 0; i< elements.length; i++){
          var parent = elements[i];
          var children = [];
          var childArray = elements[i].getElementsByTagName("mynamespace:property");
          for (let j = 0; j< childArray.length; j++){
            children.push([childArray[j].getAttribute("name"), childArray[j].getAttribute("value")])
          extensionDict[parent] = children;
        return extensionDict;

And the getters and setters (in the parts folder):

  const moddle = useService('moddle')
  const getValue = () => {
    var ext = element.businessObject.extensionElements;
    if (!ext) {
      return [];

    let prop = getProperty(element.businessObject, id);

    if(!prop ||prop.length<1){
      return [];
    return prop.value;

  const setValue = value => {
    const extensionElements = element.businessObject.extensionElements || moddle.create('bpmn:ExtensionElements')
    let prop = getProperty(element.businessObject, id);

    if (!prop) {
      prop = moddle.create('mynamespace:property', {name:id, value:value});
    prop.value = value;

   return modeling.updateProperties(element, {
1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.