如何自己实现一个丝滑的流程图绘制工具(五)bpmn的xml和json互转

背景

因为服务端给的数据并不是xml,而且服务端要拿的数据是json,所以我们只能xml和json互转,来完成和服务端的对接

xml转json
javascript 复制代码
import XML from './config/jsonxml.js'

 /**
     * xml转为json
     * @param {*} xml
     */
    xmlToJson(xml) {
      const xotree = new XML.ObjTree()
      const jsonData = xotree.parseXML(xml)
      return jsonData
    },

jsonxml.js

javascript 复制代码
const XML = function() {}

//  constructor

XML.ObjTree = function() {
  return this
}

//  class variables

XML.ObjTree.VERSION = '0.23'

//  object prototype

XML.ObjTree.prototype.xmlDecl = '<?xml version="1.0" encoding="UTF-8" ?>\n'
XML.ObjTree.prototype.attr_prefix = '-'

//  method: parseXML( xmlsource )

XML.ObjTree.prototype.parseXML = function(xml) {
  let root
  if (window.DOMParser) {
    var xmldom = new DOMParser()
    //      xmldom.async = false;           // DOMParser is always sync-mode
    const dom = xmldom.parseFromString(xml, 'application/xml')
    if (!dom) return
    root = dom.documentElement
  } else if (window.ActiveXObject) {
    xmldom = new ActiveXObject('Microsoft.XMLDOM')
    xmldom.async = false
    xmldom.loadXML(xml)
    root = xmldom.documentElement
  }
  if (!root) return
  return this.parseDOM(root)
}

//  method: parseHTTP( url, options, callback )

XML.ObjTree.prototype.parseHTTP = function(url, options, callback) {
  const myopt = {}
  for (const key in options) {
    myopt[key] = options[key] // copy object
  }
  if (!myopt.method) {
    if (
      typeof myopt.postBody === 'undefined' &&
      typeof myopt.postbody === 'undefined' &&
      typeof myopt.parameters === 'undefined'
    ) {
      myopt.method = 'get'
    } else {
      myopt.method = 'post'
    }
  }
  if (callback) {
    myopt.asynchronous = true // async-mode
    const __this = this
    const __func = callback
    const __save = myopt.onComplete
    myopt.onComplete = function(trans) {
      let tree
      if (trans && trans.responseXML && trans.responseXML.documentElement) {
        tree = __this.parseDOM(trans.responseXML.documentElement)
      }
      __func(tree, trans)
      if (__save) __save(trans)
    }
  } else {
    myopt.asynchronous = false // sync-mode
  }
  let trans
  if (typeof HTTP !== 'undefined' && HTTP.Request) {
    myopt.uri = url
    var req = new HTTP.Request(myopt) // JSAN
    if (req) trans = req.transport
  } else if (typeof Ajax !== 'undefined' && Ajax.Request) {
    var req = new Ajax.Request(url, myopt) // ptorotype.js
    if (req) trans = req.transport
  }
  if (callback) return trans
  if (trans && trans.responseXML && trans.responseXML.documentElement) {
    return this.parseDOM(trans.responseXML.documentElement)
  }
}

//  method: parseDOM( documentroot )

XML.ObjTree.prototype.parseDOM = function(root) {
  if (!root) return

  this.__force_array = {}
  if (this.force_array) {
    for (let i = 0; i < this.force_array.length; i++) {
      this.__force_array[this.force_array[i]] = 1
    }
  }

  let json = this.parseElement(root) // parse root node
  if (this.__force_array[root.nodeName]) {
    json = [json]
  }
  if (root.nodeType != 11) {
    // DOCUMENT_FRAGMENT_NODE
    const tmp = {}
    tmp[root.nodeName] = json // root nodeName
    json = tmp
  }
  return json
}

//  method: parseElement( element )

XML.ObjTree.prototype.parseElement = function(elem) {
  //  COMMENT_NODE
  if (elem.nodeType == 7) {
    return
  }

  //  TEXT_NODE CDATA_SECTION_NODE
  if (elem.nodeType == 3 || elem.nodeType == 4) {
    const bool = elem.nodeValue.match(/[^\x00-\x20]/)
    if (bool == null) return // ignore white spaces
    return elem.nodeValue
  }

  let retval
  const cnt = {}

  //  parse attributes
  if (elem.attributes && elem.attributes.length) {
    retval = {}
    for (var i = 0; i < elem.attributes.length; i++) {
      var key = elem.attributes[i].nodeName
      if (typeof key !== 'string') continue
      var val = elem.attributes[i].nodeValue
      if (!val) continue
      key = this.attr_prefix + key
      if (typeof cnt[key] === 'undefined') cnt[key] = 0
      cnt[key]++
      this.addNode(retval, key, cnt[key], val)
    }
  }

  //  parse child nodes (recursive)
  if (elem.childNodes && elem.childNodes.length) {
    let textonly = true
    if (retval) textonly = false // some attributes exists
    for (var i = 0; i < elem.childNodes.length && textonly; i++) {
      const ntype = elem.childNodes[i].nodeType
      if (ntype == 3 || ntype == 4) continue
      textonly = false
    }
    if (textonly) {
      if (!retval) retval = ''
      for (var i = 0; i < elem.childNodes.length; i++) {
        retval += elem.childNodes[i].nodeValue
      }
    } else {
      if (!retval) retval = {}
      for (var i = 0; i < elem.childNodes.length; i++) {
        var key = elem.childNodes[i].nodeName
        if (typeof key !== 'string') continue
        var val = this.parseElement(elem.childNodes[i])
        if (!val) continue
        if (typeof cnt[key] === 'undefined') cnt[key] = 0
        cnt[key]++
        this.addNode(retval, key, cnt[key], val)
      }
    }
  }
  return retval
}

//  method: addNode( hash, key, count, value )

XML.ObjTree.prototype.addNode = function(hash, key, cnts, val) {
  if (this.__force_array[key]) {
    if (cnts == 1) hash[key] = []
    hash[key][hash[key].length] = val // push
  } else if (cnts == 1) {
    // 1st sibling
    hash[key] = val
  } else if (cnts == 2) {
    // 2nd sibling
    hash[key] = [hash[key], val]
  } else {
    // 3rd sibling and more
    hash[key][hash[key].length] = val
  }
}

//  method: writeXML( tree )

XML.ObjTree.prototype.writeXML = function(tree) {
  const xml = this.hash_to_xml(null, tree)
  return this.xmlDecl + xml
}

//  method: hash_to_xml( tagName, tree )

XML.ObjTree.prototype.hash_to_xml = function(name, tree) {
  const elem = []
  const attr = []
  for (const key in tree) {
    if (!tree.hasOwnProperty(key)) continue
    const val = tree[key]
    if (key.charAt(0) != this.attr_prefix) {
      if (typeof val === 'undefined' || val == null) {
        elem[elem.length] = `<${key} />`
      } else if (typeof val === 'object' && val.constructor == Array) {
        elem[elem.length] = this.array_to_xml(key, val)
      } else if (typeof val === 'object') {
        elem[elem.length] = this.hash_to_xml(key, val)
      } else {
        elem[elem.length] = this.scalar_to_xml(key, val)
      }
    } else {
      attr[attr.length] = ` ${key.substring(1)}="${this.xml_escape(val)}"`
    }
  }
  const jattr = attr.join('')
  let jelem = elem.join('')
  if (typeof name === 'undefined' || name == null) {
    // no tag
  } else if (elem.length > 0) {
    if (jelem.match(/\n/)) {
      jelem = `<${name}${jattr}>\n${jelem}</${name}>\n`
    } else {
      jelem = `<${name}${jattr}>${jelem}</${name}>\n`
    }
  } else {
    jelem = `<${name}${jattr} />\n`
  }
  return jelem
}

//  method: array_to_xml( tagName, array )

XML.ObjTree.prototype.array_to_xml = function(name, array) {
  const out = []
  for (let i = 0; i < array.length; i++) {
    const val = array[i]
    if (typeof val === 'undefined' || val == null) {
      out[out.length] = `<${name} />`
    } else if (typeof val === 'object' && val.constructor == Array) {
      out[out.length] = this.array_to_xml(name, val)
    } else if (typeof val === 'object') {
      out[out.length] = this.hash_to_xml(name, val)
    } else {
      out[out.length] = this.scalar_to_xml(name, val)
    }
  }
  return out.join('')
}

//  method: scalar_to_xml( tagName, text )

XML.ObjTree.prototype.scalar_to_xml = function(name, text) {
  if (name == '#text') {
    return this.xml_escape(text)
  }
  return `<${name}>${this.xml_escape(text)}</${name}>\n`
}

//  method: xml_escape( text )

XML.ObjTree.prototype.xml_escape = function(text) {
  return `${text}`
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
}

export default XML

json 转为xml

javascript 复制代码
const getIncoming = (id, data) => {
  return data.filter(item => item.targetRef === id).map(items => items.id)
}
const getOutGoing = (id, data) => {
  return data.filter(item => item.sourceRef === id).map(items => items.id)
}
const getLabel = (data, labelStyle) => {
  const keyWord = ['isBold', 'isItalic', 'isStrikeThrough', 'isUnderline', 'fontFamily', 'size']
  const arr = data.filter(item => {
    return keyWord.find(key => {
      return key in labelStyle
    })
  })
  return arr.map(item => {
    const obj = {}
    keyWord.forEach(key => {
      if (labelStyle[key]) {
        obj[key === 'fontFamily' ? 'name' : key] = labelStyle[key] || ''
      }
    })
    return {
      '-id': item.id,
      'omgdc:Font': obj
    }
  })
}
export function convertJsonToBpmn(jsonData) {
  if (!jsonData || !Object.keys(jsonData).length) return {}
  const result = {
    definitions: {
      '-xmlns': 'http://www.omg.org/spec/BPMN/20100524/MODEL',
      '-xmlns:bpmndi': 'http://www.omg.org/spec/BPMN/20100524/DI',
      '-xmlns:omgdi': 'http://www.omg.org/spec/DD/20100524/DI',
      '-xmlns:omgdc': 'http://www.omg.org/spec/DD/20100524/DC',
      '-xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
      '-xmlns:bioc': 'http://bpmn.io/schema/bpmn/biocolor/1.0',
      '-xmlns:color': 'http://www.omg.org/spec/BPMN/non-normative/color/1.0',
      '-id': 'sid-38422fae-e03e-43a3-bef4-bd33b32041b2',
      '-targetNamespace': 'http://bpmn.io/bpmn',
      '-exporter': 'bpmn-js (https://demo.bpmn.io)',
      '-exporterVersion': '5.1.2',
      process: {
        '-id': 'Process_1',
        '-isExecutable': 'true',
        task: [],
        sequenceFlow: []
      },
      'bpmndi:BPMNDiagram': {
        '-id': 'BpmnDiagram_1',
        'bpmndi:BPMNPlane': {
          '-id': 'BpmnPlane_1',
          '-bpmnElement': 'Process_1',
          'bpmndi:BPMNShape': [],
          'bpmndi:BPMNEdge': []
        }
      },
      'bpmndi:BPMNLabelStyle': {}
    }
  }

  // Convert tasks
  jsonData.nodeLists.forEach(task => {
    const taskId = task.config.id
    const incoming = getIncoming(taskId, jsonData.lines)
    const outGoing = getOutGoing(taskId, jsonData.lines)
    const obj = {
      '-id': taskId
    }
    if (incoming.length > 1) {
      obj.incoming = incoming
    } else if (incoming.length === 1) {
      obj.incoming = incoming[0]
    }
    if (outGoing.length > 1) {
      obj.outgoing = outGoing
    } else if (outGoing.length === 1) {
      obj.outgoing = outGoing[0]
    }

    result.definitions.process.task.push(obj)
    const { x, y, width, height, labelStyle } = task.config

    const element = {
      '-id': `${taskId}_di`,
      '-bpmnElement': taskId,
      'omgdc:Bounds': {
        '-x': x,
        '-y': y,
        '-width': width,
        '-height': height
      },
      'bpmndi:BPMNLabel': {}
    }
    if (labelStyle && Object.keys(labelStyle).length) {
      const { x, y, width, height, id } = labelStyle
      element['bpmndi:BPMNLabel']['-labelStyle'] = id
      element['bpmndi:BPMNLabel']['omgdc:Bounds'] = {
        '-x': x,
        '-y': y,
        '-width': width,
        '-height': height
      }
      result.definitions['bpmndi:BPMNLabelStyle'] = getLabel(jsonData.nodeLists, labelStyle)
    }
    // Convert BPMN shapes
    result.definitions['bpmndi:BPMNDiagram']['bpmndi:BPMNPlane']['bpmndi:BPMNShape'].push(element)
  })

  // Convert sequence flows
  jsonData.lines.forEach(line => {
    const sequenceFlowId = line.id
    const sourceRef = line.sourceRef
    const targetRef = line.targetRef

    result.definitions.process.sequenceFlow.push({
      '-id': `${sequenceFlowId}`,
      '-name': line.name,
      '-sourceRef': sourceRef,
      '-targetRef': targetRef
    })

    // Convert BPMN edges
    result.definitions['bpmndi:BPMNDiagram']['bpmndi:BPMNPlane']['bpmndi:BPMNEdge'].push({
      '-id': `${sequenceFlowId}_di`,
      '-bpmnElement': sequenceFlowId,
      'omgdi:waypoint': line.point.map(p => {
        return { '-x': p.x, '-y': p.y }
      }),
      'bpmndi:BPMNLabel': {
        'omgdc:Bounds': {
          '-x': line.x,
          '-y': line.y,
          '-width': line.width,
          '-height': line.height
        }
      }
    })
  })

  return result
}
相关推荐
好好研究18 小时前
SpringBoot整合SpringMVC
xml·java·spring boot·后端·mvc
程途拾光15818 小时前
一键生成幼儿园安全疏散平面图设计美观合规又省心
论文阅读·安全·平面·毕业设计·流程图·课程设计·论文笔记
Yuer202519 小时前
EDCA OS 介入虚拟货币案件的标准流程图
流程图·edca os·可控ai
风流 少年19 小时前
Markdown之Mermaid流程图
流程图
EmmaXLZHONG2 天前
Reinforce Learning Concept Flow Chart (强化学习概念流程图)
人工智能·深度学习·机器学习·流程图
talle20212 天前
Hive | json数据处理
hive·hadoop·json
一起养小猫2 天前
Flutter for OpenHarmony 实战:网络请求与JSON解析完全指南
网络·jvm·spring·flutter·json·harmonyos
雪碧聊技术2 天前
JSON数据格式
json·fastjson
从此不归路2 天前
Qt5 进阶【12】JSON/XML 数据协议处理:与后端/配置文件的对接
xml·开发语言·c++·qt·json
说给风听.2 天前
零基础学 JSON:原理、语法与应用全解析
json