如何将 jsonPath 转成 对象

380 阅读1分钟

背景

在实际业务场景中,我们往往需要根据配置来生成对应的数据结构,例如将如下配置中的路径 ​resources[].image.width

{
  field: 'resources[].image.width',
  value: 200
}

转换成如下的对象结构:

{
  resources: [
    {
      image: {
        width: 200
      }
    }
  ]
}

我们的路径 resources[].image.width 类似于 jsonpath,其中 resources[] 表示 resources 字段的值是一个数组,点字符(.) 前面的字段是一个 Object 对象,如 resources[].image 表示数组 resources 里存放的是有 image 属性的对象,image.width 则表示 image 是一个有width 属性的对象。

那么我们如何将路径转换成我们想要的对象呢?

借助栈实现

栈是一种 后进先出(LIFO)的数据结构,先添加或待删除的元素都保存在栈顶,我们就利用栈的后进先出的特性,将路径 resources[].image.width 根据 [] 或 . 分割成对象的 key 放入栈中,在栈顶的元素是我们想要转换的对象的最内层的 key,栈低的元素则是我们想要转换对象的最外层的key。我们将栈中的元素逐个弹出,根据 key 的类型(Object/Array) 转换成对应的对象结构。

// 栈
class Stack {
  constructor() {
    this.items = [];
  }

  push(element) {
    this.items.push(element);
  }

  pop() {
    return this.items.pop();
  }

  peek() {
    return this.items[this.items.length - 1];
  }

  isEmpty() {
    return this.items.length === 0;
  }

  clear() {
    this.items = [];
  }

  size() {
    return this.items.length;
  }

  print() {
    //   return this.items.toString()
    return JSON.stringify(this.items)
  }
}

/**
 * 将 路径 转成对象key及对象类型(Object/Array)的对象 { dataKey: key, dataType: Object | Array }
 * 将对象 { dataKey: key, dataType: Object | Array } 压入栈中
 * @param {*} jsonpath 路径 
 */
const pathStrPushToStack = (jsonpath) => {
  const stack = new Stack()
  const dot = '.'
  let prevCharIndex = 0
  let preChar = ''
  let key = ''
  for (let i = 0; i < jsonpath.length; i++) {

    if (jsonpath[i] === dot) {

      key = jsonpath.substring(prevCharIndex, i + 1).replace(/\./g, '')
      prevCharIndex = i;
      preChar = jsonpath[i]

      if (key) {
        stack.push({ dataType: 'Object', dataKey: key })
      }
      key = ''
    } else if (jsonpath[i] === ']') {
      key = jsonpath.substring(prevCharIndex, i + 1).replace(/(\.|\[|\])/g, '')
      prevCharIndex = i + 1;
      preChar = jsonpath[i]

      if (key) {
        stack.push({ dataType: 'Array', dataKey: key })
      }
      key = ''
    } else {
      key += jsonpath[i]
      if (i === jsonpath.length - 1) {
        stack.push({ dataType: preChar === dot ? 'Object' : "Array", dataKey: key })
      }
    }

  }
  return stack
}

/**
 * 将 路径 转换成目标对象
 * @param {*} jsonpath  路径
 * @param {*} value 最内层字段需要设置的值
 */
const jsonpathToObj = (jsonpath, value) => {

  // 将路径转换成{key, dataType} 对象放入栈中
  const stack = pathStrPushToStack(jsonpath)

  let resultObj = undefined
  const size = stack.length
  // 依次从栈中取出栈顶元素,转换成对象
  for (let i = 0; i < size; i++) {
    const stackEle = stack.pop()
    if (stackEle.dataType === 'Object') {
      if (!resultObj) {
        resultObj = { [stackEle.dataKey]: value }
      } else {
        resultObj = { [stackEle.dataKey]: resultObj }
      }
    }
    else if (stackEle.dataType === 'Array') {
      resultObj = { [stackEle.dataKey]: [resultObj] }
    }
  }
  return resultObj
}