js 实现比对 新旧json数据的变化

494 阅读2分钟

问题产生

刚学习的netty,就想尝试着共享文档怎么做,于是在写的过程中发现需要一个 diff 操作,即比较两份新旧json数据的不一致,然后与共享的小伙伴进行文档同步,于是就简单的写了个demo

import { JsonArray, JsonObject } from "type-fest";
/*
 * @Description:
 * @Date: 2022-10-06 14:56:52
 * @LastEditTime: 2022-10-06 15:56:42
 * @FilePath: \scm-vue\src\hooks\json-diff\index.ts
 */

interface optData {
  // 操作的方法
  opt: string;
  // 全路径
  fullPath: string;
  attributeName: string;
  context: any;
  deep: number;
}

/**
 * 获取数据类型
 * @param {*} obj
 */
function getTypeByObj(obj: Object) {
  return Object.prototype.toString
    .call(obj)
    .match(/^\[object ([a-zA-Z]*)\]$/)[1];
}
/**
 * 判断是否是空对象
 * @param {*} obj
 */
function isEmptyObject(obj: any) {
  for (var key in obj) {
    return false;
  }
  return true;
}

function findAddandChangediff(
  newJson: JsonObject | JsonArray,
  oldJson: JsonObject | JsonArray,
  deep: number,
  fullPath: string
) {
  // 判断两个json不为空
  if (
    !newJson ||
    isEmptyObject(newJson) ||
    !oldJson ||
    isEmptyObject(oldJson)
  ) {
    return null;
  }

  let opt = new Array<optData>();
  for (let attributeName in newJson) {
    // 判断 是否是新增  如果在老文档中不存在就是新增
    if (!oldJson[attributeName]) {
      opt.push({
        opt: "add",
        fullPath: fullPath,
        attributeName: attributeName,
        context: newJson[attributeName],
        deep: deep,
      });
    }

    // 判断数据类型是否一致
    else if (
      getTypeByObj(oldJson[attributeName]) ===
      getTypeByObj(newJson[attributeName])
    ) {
      //由于 json 可以嵌套结构 因此需要递归
      if (
        getTypeByObj(oldJson[attributeName]) === "Array" ||
        getTypeByObj(oldJson[attributeName]) === "Object"
      ) {
        // 递归
        const diffData = findAddandChangediff(
          newJson[attributeName],
          oldJson[attributeName],
          deep + 1,
          fullPath + "/" + attributeName
        );
        if (!isEmptyObject(diffData)) {
          opt.push(...diffData);
        }

        // 如果内容不一致就是修改了
      } else if (newJson[attributeName] !== oldJson[attributeName]) {
        opt.push({
          opt: "change",
          fullPath: fullPath,
          attributeName: attributeName,
          context: newJson[attributeName],
          deep: deep,
        });
      }
      // 数据类型不一致也是修改了
    } else {
      opt.push({
        opt: "change",
        fullPath: fullPath,
        attributeName: attributeName,
        context: newJson[attributeName],
        deep: deep,
      });
    }
  }

  return opt;
}

// 找到删除的节点  只需要将上面的代码的 入参 改变一下就可以得到 删除的节点了
function findRemoveDiff(
  newJson: JsonObject | JsonArray,
  oldJson: JsonObject | JsonArray
) {
  const data = findAddandChangediff(oldJson, newJson, 0, "").filter(
    (item) => item.opt === "add"
  );

  data.forEach(function (item) {
    item.opt = "delete";
  });
  return data;
}

// 外面调用此方法就可以得到增加 修改 删除的节点分别是什么了
function diff(
  newJson: JsonObject | JsonArray,
  oldJson: JsonObject | JsonArray,
  deep: number,
  fullPath: string
) {
  return [
    ...findAddandChangediff(newJson, oldJson, deep, fullPath),
    ...findRemoveDiff(newJson, oldJson),
  ];
}

export { diff };

测试方法

let newJson = [
  {
    name: "aichen",
    title: "title",
    lv2: {
      tip: "lv2-tip",
      lv31: {
        msg: "lv3-msg1",
      },
    },
    add: "新增",
  },
];
let oldJson = [
  {
    name: "aichen1",
    age: 18,
    title: "title",
    lv2: {
      tip: "lv2-tip",
      lv3: {
        msg: "lv3-msg",
      },
    },
  },
];

const res = diff(newJson, oldJson, 0, "");

console.log(res);

数据结果

image.png