调用API返回含重复键的JSON,如何将重复键转换为数组?
解决JSON重复键转数组的问题
为啥reviver拿不到所有重复键?
标准JSON规范里不允许对象存在重复键,JS自带的JSON.parse解析时会直接覆盖前面的重复键值对,只保留最后一个,所以你的reviver函数只能拿到最后一个line的值——这不是bug,是规范要求的行为。
怎么处理这种非标准JSON?
要把重复键转成数组,得先预处理JSON字符串,或者写个自定义解析器,下面给两种实用方案:
方法一:正则预处理字符串(适合简单结构)
用正则匹配重复键值对,直接把它们合并成数组格式:
const rawJson = '{"address":{"city":"Toronto", "line":"ln1","line":"ln2","line":"ln3"}}'; const tempMap = {}; // 第一步:把重复键的第一个值改成数组开头 let processed = rawJson.replace(/"([^"]+)":\s*"([^"]+)"(?=\s*,"\1":)/g, (match, key, val) => { if (!tempMap[key]) { tempMap[key] = true; return `"${key}": ["${val}"`; } return `,"${val}"`; }); // 第二步:给数组补上结尾的] processed = processed.replace(/"([^"]+)":\s*\["([^"]+)"(.*?)(?=\s*(}|,))/g, (match, key) => { tempMap[key] = false; return `${match}]`; }); // 解析处理后的标准JSON const result = JSON.parse(processed); console.log(result);
方法二:自定义解析器(复杂场景适用)
如果JSON嵌套深、包含转义字符等特殊情况,正则搞不定,就自己写个逐字符解析的函数,遇到重复键就自动转成数组:
function parseDuplicateKeyJson(jsonStr) { let idx = 0; const len = jsonStr.length; // 跳过空格 const skipWs = () => { while (idx < len && /\s/.test(jsonStr[idx])) idx++; }; // 解析字符串(处理转义字符) const parseStr = () => { idx++; // 跳过开头的" let str = ''; while (idx < len && jsonStr[idx] !== '"') { if (jsonStr[idx] === '\\') { idx++; str += jsonStr[idx]; } str += jsonStr[idx++]; } idx++; // 跳过结尾的" return str; }; // 解析对象(核心处理重复键) const parseObj = () => { idx++; // 跳过{ const obj = {}; skipWs(); while (idx < len && jsonStr[idx] !== '}') { const key = parseStr(); skipWs(); idx++; // 跳过: const val = parseValue(); skipWs(); // 重复键处理:已存在则转数组,不存在直接赋值 if (obj[key]) { if (!Array.isArray(obj[key])) obj[key] = [obj[key]]; obj[key].push(val); } else { obj[key] = val; } if (jsonStr[idx] === ',') idx++; // 跳过分隔符, skipWs(); } idx++; // 跳过} return obj; }; // 解析数组、布尔值、null、数字的逻辑(和标准解析一致) const parseArr = () => { idx++; const arr = []; skipWs(); while (idx < len && jsonStr[idx] !== ']') { arr.push(parseValue()); if (jsonStr[idx] === ',') idx++; skipWs(); } idx++; return arr; }; const parseBool = () => { const boolStr = jsonStr.slice(idx, idx + 4); idx += boolStr.length; return boolStr === 'true'; }; const parseNull = () => { idx += 4; return null; }; const parseNum = () => { let numStr = ''; while (idx < len && /[0-9.e+-]/.test(jsonStr[idx])) numStr += jsonStr[idx++]; return Number(numStr); }; // 统一调度解析逻辑 const parseValue = () => { skipWs(); const char = jsonStr[idx]; if (char === '{') return parseObj(); if (char === '[') return parseArr(); if (char === '"') return parseStr(); if (char === 't' || char === 'f') return parseBool(); if (char === 'n') return parseNull(); return parseNum(); }; return parseValue(); } // 使用示例 const rawJson = '{"address":{"city":"Toronto", "line":"ln1","line":"ln2","line":"ln3"}}'; const result = parseDuplicateKeyJson(rawJson); console.log(result);
额外提醒
这种带重复键的JSON属于非标准格式,优先建议和供应商沟通,让他们改成标准JSON返回(比如直接把line做成数组),这才是最稳妥的解决办法。自己处理的话,要注意覆盖转义字符、嵌套结构等边界场景。
内容的提问来源于stack exchange,提问作者Reza
相关产品推荐
相关产品推荐

