前言
在上一篇文章 手把手教你玩转BpmnJS元素属性更新 一文中,跟大家讲解了 如何通过已有的JSON schema 定义与完整 XML 来解析属性结构 和 通过两个官方 API 更新属性的方式及注意事项。
那么假设现在只有一个 JSON Schema 文件,怎么判断属性的构成呢?文本就带大家分析 如何从JSON Schema 判断属性结构。
1. 分析 JSON Schema
该文件的每个字段的具体定义和编写方式,具体请查看 BpmnJS自定义描述文件说明。但是因为平时了解到大家对自定义这一块一般需求比较少,所以这里就用 camunda 团队新出的zeebe引擎来举例吧,这也是讨论群里一个小伙伴问我的问题。
完整的 JSON 文件请查看bpmn-process-designer/packages/moddle-extensions/zeebe.json,这里节选了ioMapping 输入输出配置
{ types: [ { "name": "ZeebeServiceTask", "extends": [ "bpmn:ServiceTask", "bpmn:BusinessRuleTask", "bpmn:ScriptTask", "bpmn:SendTask", "bpmn:EndEvent", "bpmn:IntermediateThrowEvent" ], "properties": [ { "name": "retryCounter", "type": "String", "isAttr": true } ] }, { "name": "IoMapping", "superClass": [ "Element" ], "properties": [ { "name": "ioMapping", "type": "IoMapping" }, { "name": "inputParameters", "isMany": true, "type": "Input" }, { "name": "outputParameters", "isMany": true, "type": "Output" } ], "meta": { "allowedIn": [ "bpmn:CallActivity", "bpmn:Event", "bpmn:ReceiveTask", "zeebe:ZeebeServiceTask", "bpmn:SubProcess" ] } }, { "name": "InputOutputParameter", "properties": [ { "name": "source", "isAttr": true, "type": "String" }, { "name": "target", "isAttr": true, "type": "String" } ] }, { "name": "Input", "superClass": [ "InputOutputParameter" ], "meta": { "allowedIn": [ "bpmn:CallActivity", "zeebe:ZeebeServiceTask", "bpmn:SubProcess" ] } }, { "name": "Output", "superClass": [ "InputOutputParameter" ], "meta": { "allowedIn": [ "bpmn:CallActivity", "bpmn:Event", "bpmn:ReceiveTask", "zeebe:ZeebeServiceTask", "bpmn:SubProcess" ] } } ] }
🚀 这里也算是一点小技巧:只要是 types 数组中的某一个定义的properties 属性配置中,如果这个属性的 type 不是 Number、Boolean、String 这几个基础类型,而是 JSON Schema 中定义的类型的话,通常这个属性都是绑定一个 通过 moddle.create 创建的对应类型的实例
1.1 分析 IoMapping
首先查看这个类型的 supperClass 或者 extends 字段,这里定义的是supperClass:[Element],说明这个属性基本上在 XML 文件中都体现为一个标签,并且标签名为zeebe:ioMapping。
然后就是 meta 字段,一般这个字段里面会有一个 allowedIn 的数组格式的字段,表示 该属性或者标签(IoMapping)允许被配置/插入哪些类型的元素/标签中,通过 通配符 * 表示允许插入所有标签/属性中。此时根据配置 IoMapping 标签可以被插入到 SubProcess 子流程、CallActivity 调用任务、zeebe:ZeebeServiceTask Zeebe服务任务中。
而zeebe:ZeebeServiceTask的定义中,则是用extend来 扩展原来的ServiceTas等类型元素的,所以IoMapping 定义里 allowedIn 中的 ZeebeServiceTask 其实就可以理解为 ServiceTask 等元素的替代指示。
最后分析 properties 配置,可以看到IoMapping 接收三个属性配置:
- ioMapping:类型就是IoMapping,表示这个标签内部还可以插入 IoMapping 标签,即支持嵌套
- inputParameters:类型为Input,且isMany 为 true,表示这里是一个 数组形式,在 js 中体现为**
inputParameters: []
**
- outputParameters:类型为Output,其他的信息与inputParameters一致
总结:
IoMapping 在 xml 中体现为一个标签形式,内部可以继续插入 IoMapping 标签;并且接收其他两个属性 inputParameters 和 outputParameters。
并且 inputParameters 与 outputParameters 都不是简单的数据类型,而且是数组格式,所以给这两个属性设置属性值时,内部的属性也需要通过 moddle.create 创建实例。
在我们创建一个 IoMapping 实例时,步骤和结果如下:
const moddle = modeler.get('moddle') const ioMapping = moddle.create('zeebe:IoMapping', {inputParameters: [], outputParameters: []}) // 结果如下: IoMapping = { $type: 'zeebe:IoMapping', inputParameters: [], outputParameters: [] }
1.2 ZeebeServiceTask 没有 IoMapping
虽然分析了 IoMapping 之后,知道了这个属性在配置时该怎么操作;但是,在zeebe:ZeebeServiceTask的定义中,并没有体现出具有IoMapping属性的配置,此时我们可以查询 zeebeServiceTask 定义中的 extend 中的所有元素类型定义,看看有没有 IoMapping 配置。
当然:结果是没有找到。
那么此时这个 IoMapping 属性该怎么配置到serviceTask节点上呢?
嗯~~~ 经过之前研究了一番他们的属性面板,发现这种情况下默认会挂载到 extensionElements 属性中,所以假设我们为一个ServiceTask节点添加了一个 IoMapping 配置的话,此时该节点的 businessObject 会变成这样:
// 只是提示内容格式,不要真的这么赋值哈 serviceTask.businessObject = { extensionElements: { $type: "bpmn:ExtensionElements", values: [ { $type: "zeebe:IoMapping", inputParameters: [], outputParameters: [] } ] } }
在 xml 中体现为:
<bpmn:serviceTask id="Activity_0py009y"> <bpmn:extensionElements> <zeebe:ioMapping></zeebe:ioMapping> </bpmn:extensionElements> </bpmn:serviceTask>
1.3 Input 与 Output
现在我们在回过头来看一下 IoMapping 的两个 properties 属性配置,其参数配置都是自定义类型,一个是Input,一个是Output。
而在上面的 JSON Schema 文件中Input 和 Output 都是继承的 InputOutputParameter 类型,具有两个 字符串格式 的参数 source 与 target。
但是因为在 JSON Schema 文件中已经对 InputOutputParameter 重新定义成了两个类型 Input 与 Output,并且在 IoMapping 的定义中也没有使用 InputOutputParameter,所以我们在编写 js 代码的时候也要注意moddle.create 时传入的参数必须是 properties 配置中定义的那个类型名。
那么此时我们给 IoMapping 的 inputParameters 与 outputParameters 设置值时,可以这么操作:
const moddle = modeler.get('moddle') const input1 = moddle.create('zeebe:Input', {source: '=xxx1', target: 'target1'}) const input2 = moddle.create('zeebe:Input', {source: '=xxx2', target: 'target2'}) const output1 = moddle.create('zeebe:Output', {source: '=xxx3', target: 'target3'}) const ioMapping = moddle.create('zeebe:IoMapping', { inputParameters: [input1, input2], outputParameters: [output1] } ) // 需要根据情况判断 extensionElements 的存在情况,这里只是演示就不判断了,直接更新 modeling.updateModdleProperties(element, extensionElements, { values: [...extensionElements.get("values"), ioMapping] } );
此时我们会得到一个类似这样的 xml
<bpmn:serviceTask id="Activity_0py009y"> <bpmn:extensionElements> <zeebe:ioMapping> <zeebe:input source="=xxx1" target="InputVariable_3mqm1gd" /> <zeebe:input source="=xxx2" target="InputVariable_3mqm1gd" /> <zeebe:output source="=xxx3" target="OutputVariable_01h6ldb" /> </zeebe:ioMapping> </bpmn:extensionElements> <bpmn:incoming>Flow_0nx7hug</bpmn:incoming> </bpmn:serviceTask>
这里说明一下, xml 是通过官方的编辑器生成的,所以 target 字段会默认生成一个类似 hashID 的值;在实际项目中也可以参考这部分
最后
到此,通过 JSON Schema 分析元素属性结构的过程就基本结束了。总的来说,虽然可以通过这样的方式去分析出来,但是依旧没有通过到导入一个 xml 直接打印属性结构来的快。只是增加一个这样的分析过程,能加深我们对这个 JSON Schema 配置的理解,在后面需要配置和定义项目特殊的属性的时候,也能更加快速的配置出来。