You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JSON.stringify忽略数组自定义键,如何保留混合结构的键值对?

如何在JSON序列化时保留数组的自定义键值对?

这个问题我之前也踩过坑,核心原因是JSON的数组规范和JavaScript数组的特性不匹配:

JavaScript里的数组本质是特殊的对象,所以你可以给它添加任意非数字索引的属性;但JSON的数组类型只认可连续的数字索引(从0开始)对应的元素,所有非数字键的属性都会在JSON.stringify时被直接忽略,这就是你看到x、y丢失的原因。

下面给你几个可行的解决方案,按推荐程度排序:

1. 改用对象包裹结构(最推荐)

既然混合数组+自定义属性的结构不符合JSON规范,不如直接把数据拆分成“数组元素”和“自定义属性”两部分,放在一个普通对象里。这样结构清晰,序列化完全没问题,也符合所有JSON解析器的预期:

// 重构你的数据结构
const data = {
  items: [12, 15], // 原来的数组元素
  x: 12,
  y: 15
};

// 序列化结果完整保留所有数据
JSON.stringify(data); 
// 输出:{"items":[12,15],"x":12,"y":15}

// 访问方式也很直观
console.log(data.items[0]); // 12
console.log(data.x); // 12

2. 自定义JSON.stringify的replacer函数(保留原结构的折中方案)

如果你一定要维持原有的数组+自定义属性结构,可以通过自定义replacer函数,手动把数组的非数字属性合并到一个对象里再序列化。注意:这样序列化后的结果会是对象类型(因为JSON不支持带自定义属性的数组),之后解析时需要再转回去:

序列化时的replacer函数

function arrayWithPropsReplacer(key, value) {
  if (Array.isArray(value)) {
    const result = {};
    // 先复制数组的数字索引元素
    for (let i = 0; i < value.length; i++) {
      result[i] = value[i];
    }
    // 再复制所有非数字的可枚举属性
    for (const prop in value) {
      if (!/^\d+$/.test(prop)) {
        result[prop] = value[prop];
      }
    }
    return result;
  }
  return value;
}

// 你的原数组
const arrObj = [];
arrObj["x"] = 12;
arrObj.push(12);
arrObj["y"] = 15;
arrObj.push(15);

// 序列化后得到包含所有键的对象
const serialized = JSON.stringify(arrObj, arrayWithPropsReplacer);
// 输出:{"0":12,"1":15,"x":12,"y":15}

解析时的reviver函数(还原回原结构)

如果需要把序列化后的字符串解析回原来的混合结构,可以搭配reviver函数:

function arrayWithPropsReviver(key, value) {
  if (typeof value === 'object' && value !== null) {
    const keys = Object.keys(value);
    // 检查是否有连续的数字索引(从0开始)
    const numericKeys = keys.filter(k => /^\d+$/.test(k)).map(Number);
    if (numericKeys.length > 0 && 
        Math.min(...numericKeys) === 0 && 
        Math.max(...numericKeys) === numericKeys.length - 1) {
      const arr = [];
      // 先添加数组元素
      numericKeys.forEach(k => arr[k] = value[k]);
      // 再添加非数字属性
      keys.forEach(k => {
        if (!/^\d+$/.test(k)) {
          arr[k] = value[k];
        }
      });
      return arr;
    }
  }
  return value;
}

// 解析回原结构
const parsedArr = JSON.parse(serialized, arrayWithPropsReviver);
console.log(parsedArr[0]); // 12
console.log(parsedArr.x); // 12

3. 额外提醒

虽然JavaScript允许给数组加自定义属性,但这种写法并不推荐:

  • 很多数组方法(比如map、filter、forEach)只会处理数字索引的元素,容易造成逻辑混淆;
  • 这种结构不符合常规的代码规范,其他开发者维护时容易踩坑;
  • 除了JSON序列化,其他工具/库也可能不识别这些自定义属性。

所以优先考虑第一种重构方案,代码会更健壮、易维护。

内容的提问来源于stack exchange,提问作者ConductedClever

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 06:32:54