如何从带路径的键值对字典重建嵌套JSON?代码问题排查
问题:从带路径标记的键值对重建JSON结构
背景
需要从带路径标记的键值对字典重建类JSON结构:
- 键代表叶子节点的完整路径,值为对应位置的数据集;
- 键中的
|TRUE|/|FALSE|标记该节点是否为数组类型。
输入的键值对字典
{ FirstName: [ 'Penelope' ], LastName: [ 'Veum' ], BusinessPartnerCategory: [ '1' ], SearchTerm1: [ 'DZBANK456' ], CorrespondenceLanguage: [ 'EN' ], 'to_Customer|TRUE|.InternationalLocationNumber1': [ '0' ], 'to_Customer|TRUE|.CustomerAccountGroup': [ 'CUST' ], 'to_Customer|TRUE|.to_CustomerSalesArea|FALSE|.results|TRUE|.SalesOrganization': [ '1710' ], 'to_Customer|TRUE|.to_CustomerSalesArea|FALSE|.results|TRUE|.DistributionChannel': [ '10' ], 'to_Customer|TRUE|.to_CustomerSalesArea|FALSE|.results|TRUE|.Division': [ '00' ], 'to_Customer|TRUE|.to_CustomerSalesArea|FALSE|.results|TRUE|.Currency': [ 'EUR' ], 'to_Customer|TRUE|.to_CustomerSalesArea|FALSE|.results|TRUE|.ItemOrderProbabilityInPercent': [ '100' ], 'to_Customer|TRUE|.to_CustomerSalesArea|FALSE|.results|TRUE|.CompleteDeliveryIsDefined': [ false ], 'to_BusinessPartnerAddress|FALSE|.results|TRUE|.Country': [ 'US' ], 'to_BusinessPartnerAddress|FALSE|.results|TRUE|.CityName': [ 'Atlanta' ], 'to_BusinessPartnerAddress|FALSE|.results|TRUE|.Region': [ 'GA' ], 'to_BusinessPartnerAddress|FALSE|.results|TRUE|.PostalCode': [ '30303' ], 'to_BusinessPartnerAddress|FALSE|.results|TRUE|.StreetName': [ '543 Peachtree Street' ], 'to_BusinessPartnerAddress|FALSE|.results|TRUE|.to_EmailAddress|FALSE|.results|TRUE|.IsDefaultEmailAddress': [ true ], 'to_BusinessPartnerAddress|FALSE|.results|TRUE|.to_EmailAddress|FALSE|.results|TRUE|.EmailAddress': [ '{{FirstName}}.{{LastName}}@gmail.com' ], 'to_BusinessPartnerAddress|FALSE|.results|TRUE|.to_EmailAddress|FALSE|.results|TRUE|.AddressCommunicationRemarkText': [ 'the' ], 'to_BusinessPartnerRole|FALSE|.results|TRUE|.BusinessPartnerRole': [ 'FLCU00' ] }
预期输出
{ "FirstName": "Jarrell", "LastName": "Bode", "BusinessPartnerCategory": "1", "SearchTerm1": "DZBANK456", "CorrespondenceLanguage": "EN", "to_BusinessPartnerRole": { "results": [ { "BusinessPartnerRole": "FLCU00" } ] }, "to_BusinessPartnerAddress": { "results": [ { "to_EmailAddress": { "results": [ { "IsDefaultEmailAddress": true, "EmailAddress": "Jarrell.Bode@gmail.com", "AddressCommunicationRemarkText": "whoa" } ] }, "Country": "US", "CityName": "Atlanta", "Region": "GA", "PostalCode": "30303", "StreetName": "543 Peachtree Street" } ] }, "to_Customer": [ { "InternationalLocationNumber1": "0", "to_CustomerSalesArea": { "results": [ { "SalesOrganization": "1710", "DistributionChannel": "10", "Division": "00", "Currency": "USD", "ItemOrderProbabilityInPercent": "100", "CompleteDeliveryIsDefined": false } ] }, "CustomerAccountGroup": "CUST" } ] }
现有代码
const final_result = []; for (let index = 0; index < quantity; index++) { let response_cpy = Object.assign({}, response_new); for (const [path, data] of Object.entries(pathDataDict)) { const pathSplit = path.split("."); pathSplit.reduce(function (response: any, incompleteKey: any) { const keySplit = incompleteKey.split("|"); const actualKey = keySplit[0]; if (actualKey === pathSplit[pathSplit.length - 1]) { if ( typeof response === "object" && !Array.isArray(response) && response !== null ) { return (response[actualKey] = (data as any)[index]); } else { return (response[0][actualKey] = (data as any)[index]); } } else { return ( response[actualKey] || (keySplit.length > 1 && keySplit[1] === "TRUE" ? (response[actualKey] = [{}]) : (response[actualKey] = {})) ); } }, response_cpy); } final_result.push(response_cpy); }
现有代码问题:生成猴子补丁属性,非叶子节点丢失,数组出现异常属性。
代码问题分析
- reduce回调返回值错误:处理叶子节点时,返回的是赋值后的叶子值而非当前父节点,导致后续同一父节点的其他叶子路径处理时,上下文变成了叶子值,进而在基础类型上添加属性,出现猴子补丁。
- 叶子节点判断逻辑错误:用
actualKey === pathSplit[pathSplit.length - 1]判断是否为叶子节点,会混淆同名节点的层级,无法准确区分当前处理的片段是否为路径的最后一级。 - 数组节点处理不严谨:直接取数组的
response[0]但未检查数组是否为空,且切换上下文时未正确定位到数组内部的对象,导致属性错误挂载到数组本身而非数组内的元素。
修复后的代码实现
function buildJsonStructure(pathDataDict: Record<string, any[]>, quantity: number): any[] { const finalResult: any[] = []; for (let index = 0; index < quantity; index++) { const result: any = {}; for (const [path, dataList] of Object.entries(pathDataDict)) { const pathSegments = path.split("."); let currentContext = result; for (let i = 0; i < pathSegments.length; i++) { const segment = pathSegments[i]; const [key, isArrayStr] = segment.split("|"); const isArray = isArrayStr === "TRUE"; const isLastSegment = i === pathSegments.length - 1; if (isLastSegment) { // 处理叶子节点:赋值对应数据 if (Array.isArray(currentContext)) { // 数组上下文确保有元素可赋值 if (currentContext.length === 0) currentContext.push({}); currentContext[0][key] = dataList[index]; } else { currentContext[key] = dataList[index]; } } else { // 处理非叶子节点:确保结构存在并切换上下文 if (!currentContext[key]) { currentContext[key] = isArray ? [{}] : {}; } // 切换到子节点上下文:数组取第一个元素,对象直接取节点 currentContext = Array.isArray(currentContext[key]) ? currentContext[key][0] : currentContext[key]; } } } finalResult.push(result); } return finalResult; } // 使用示例 // const output = buildJsonStructure(pathDataDict, quantity); // console.log(JSON.stringify(output, null, 2));
修复说明
- 替换reduce为普通循环:用for循环遍历路径片段,直观控制上下文切换,避免reduce返回值导致的上下文混乱。
- 准确判断叶子节点:通过迭代索引
i === pathSegments.length - 1判断是否为路径最后一级,彻底解决同名节点的层级混淆问题。 - 严谨维护数组结构:
- 创建数组节点时直接初始化
[{}],确保内部有可承载属性的空对象; - 处理叶子节点时先检查数组是否为空,为空则添加元素再赋值。
- 创建数组节点时直接初始化
- 正确切换上下文:每次处理完非叶子节点后,将上下文切换到节点内部(数组取第一个元素,对象直接取节点),确保后续路径在正确层级处理。
内容的提问来源于stack exchange,提问作者CodingAddict
相关产品推荐
相关产品推荐

