JSON对象平铺和平铺后对象化的实现学习

1.什么是JSON对象平铺与对象化

常见的json结构如下:

let jdata = {"a": "a1","b": {"b1": "b11"},"c": ["c1", "c2", "c3"],"d": [{"d1": "d2"}]
}

平铺即用单层的 (Key,Value) 结构表示当前这种递归结构,平铺后效果如下:

{"a":"a1","b.b1":"b11","c[0]":"c1","c[1]":"c2","c[2]":"c3","d[0].d1":"d2"
}

而平铺对象化就是将原来的平铺结构还原为原始对象。

2. 平铺的实现

json对象中的v可以是一个对象、数组、基本数值类型。而数组中的v依然满足第一条定义。

以第一节的结构为例,平铺过程既是递归的解析每个v,直到v是一个基本结构(数字、字符串、布尔值)不可拆分。随后用路径作为key,最后不可分的值作为v。

根据上述思路代码实现如下:

Object.jsonFlatten = function(data) {let result = {};function deepTraverse(obj, path) {if (obj !== Object(obj)) {result[path] = obj;return;}// 数组对象,如果前缀路径保证正确,拼接上下标访问符即可if (Array.isArray(obj)) {for (let i = 0; i < obj.length; i++) {deepTraverse(obj[i], path + "[" + i + "]");}return;}// json对象for (let i in obj) {if (path !== "") {deepTraverse(obj[i], path + "." + i);} else {deepTraverse(obj[i], i);}}}if(data !== Object(data)) {return data;}deepTraverse(data,"");return result;
}

3.平铺数据对象化的实现

平铺后得到的结果是一个单层的(key,value)结构。对象化即是将key还原成
原始的对象结构。

首先分析key的组成分如下几种:

  • 嵌套对象结构:x.y.z
  • 对象加数组: x.y[0],
  • 对象加嵌套数组: x.y[0][0]

接着我们对key进行解析,拆分处我们要的元素, 对象或数组。
以x.y[0]为例:

我们需要拆分出 x、y、[0]三个元素。由于key是一个字符串,因此需要做一个简单的词法分析。通过正则表达式,解析出需要的词。

限定对象名只能是 非 . [ ]三个字符之外的符号构成,
x 可以是一个对象 .y 结构中的y是一个对象,[0]是一个数组。

因此解析分两类:

\.?([^.\[\]]+)   解析 x 、.y对象结构
\[\d+\]          解析数组

对于一串符号,我们期待每解析出一个元素,就去还原我们需要的结构。对于x.y[0]还原过程如下:

解析出x

{x: {}
}

解析出 y

{x: {y:{}}
}

解析出[0]

{x: {y:[0]}
}

最后通过当前指针修改数组中的值。

这时我们发现一个问题 解析出 y -> [0]的过程,按正常顺序解析出y,没法直接判断y的value是一个对象还是数组。 因此我们需要前看一个符号进行判断。 或者转换思考,我们所填的对象均是某个key的value中存放的对象的一个key。

这样转换思考后,b[0],这种形式就可以统一进行处理了。

本例中,解析出y时,我们前看一个符号 [0],发现是数组,这时y的value填充为数组,反之填充为对象。

为了统一化处理问题,我们引入一个初始对象{} 和一个默认的key “” (换成任意不冲突符号都可以)

初始:

{"":
}

随后党我们解析目标key(x.y[0])时, 每遇到一个符号就相当于是前看符号的作用。

遇到x


{"": {}
}

遇到y

{"":{x:{}}
}

遇到[0]

{"":{x:{y:[]}}
}

最后一步填充值,原来的key,拿到值,填充到 最后的数组中[]。

上述描述清楚了思路,如问期望所述,我们期望每个符号解析到了,在字符列表中继续解析下一个符号。这个能力在不同语言中实现不同。以JavaScript为例,我们会用到正则表达式的exec方法。exec方法[1]说明如下,我们写正则表达式时,需要用到全局匹配。

exec
说明
exec() 方法的功能非常强大,它是一个通用的方法,而且使用起来也比 test() 方法以及支持正则表达式的 String 对象的方法更为复杂。如果 exec() 找到了匹配的文本,则返回一个结果数组。否则,返回 null。此数组的第 0 个元素是与正则表达式相匹配的文本,第 1 个元素是与 RegExpObject 的第 1 个子表达式相匹配的文本(如果有的话),第 2 个元素是与 RegExpObject 的第 2 个子表达式相匹配的文本(如果有的话),以此类推。除了数组元素和 length 属性之外,exec() 方法还返回两个属性。index 属性声明的是匹配文本的第一个字符的位置。input 属性则存放的是被检索的字符串 string。我们可以看得出,在调用非全局的 RegExp 对象的 exec() 方法时,返回的数组与调用方法 String.match() 返回的数组是相同的。但是,当 RegExpObject 是一个全局正则表达式时,exec() 的行为就稍微复杂一些。它会在 RegExpObject 的 lastIndex 属性指定的字符处开始检索字符串 string。当 exec() 找到了与表达式相匹配的文本时,在匹配后,它将把 RegExpObject 的 lastIndex 属性设置为匹配文本的最后一个字符的下一个位置。这就是说,您可以通过反复调用 exec() 方法来遍历字符串中的所有匹配文本。当 exec() 再也找不到匹配的文本时,它将返回 null,并把 lastIndex 属性重置为 0。

通过上述分析javascript实现如下:

Object.unflatten = function(data) {"use strict";if (Object(data) !== data || Array.isArray(data))return data;var regex = /\.?([^.\[\]]+)|\[(\d+)\]/g,resultholder = {};for (var p in data) {var cur = resultholder,prop = "",m;while (m = regex.exec(p)) {cur = cur[prop] || (cur[prop] = (m[2] ? [] : {}));prop = m[2] || m[1];}cur[prop] = data[p];}return resultholder[""] || resultholder;
};

4. 总结

分析平铺前的结构特点和平铺后的结构特点,在还原时利用额外数据简化前看的操作,简化代码

参考

[1]exec方法说明,https://www.w3school.com.cn/jsref/jsref_exec_regexp.asp
[2]https://stackoverflow.com/questions/19098797/fastest-way-to-flatten-un-flatten-nested-json-objects


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部