展平/取消展平嵌套 JSON 对象的最快方法
Posted
技术标签:
【中文标题】展平/取消展平嵌套 JSON 对象的最快方法【英文标题】:Fastest way to flatten / un-flatten nested JSON objects 【发布时间】:2013-10-06 14:04:33 【问题描述】:我将一些代码放在一起来展平和取消展平复杂/嵌套的 JSON 对象。它可以工作,但有点慢(触发“长脚本”警告)。
对于我想要的扁平名称“。”作为数组的分隔符和[INDEX]。
例子:
un-flattened | flattened
---------------------------
foo:bar:false => "foo.bar":false
a:[b:["c","d"]] => "a[0].b[0]":"c","a[0].b[1]":"d"
[1,[2,[3,4],5],6] => "[0]":1,"[1].[0]":2,"[1].[1].[0]":3,"[1].[1].[1]":4,"[1].[2]":5,"[2]":6
我创建了一个 ~ 模拟我的用例 http://jsfiddle.net/WSzec/ 的基准测试
获取嵌套的 JSON 对象 展平 查看并可能在展平时对其进行修改 将其展开回原来的嵌套格式以便运走我想要更快的代码:为了澄清,在 IE 9+、FF 24+ 和 Chrome 29+ 中完成 JSFiddle 基准测试 (http://jsfiddle.net/WSzec/) 的代码明显更快(~20%+ 会很好) .
以下是相关的 javascript 代码:当前最快:http://jsfiddle.net/WSzec/6/
JSON.unflatten = function(data)
"use strict";
if (Object(data) !== data || Array.isArray(data))
return data;
var result = , cur, prop, idx, last, temp;
for(var p in data)
cur = result, prop = "", last = 0;
do
idx = p.indexOf(".", last);
temp = p.substring(last, idx !== -1 ? idx : undefined);
cur = cur[prop] || (cur[prop] = (!isNaN(parseInt(temp)) ? [] : ));
prop = temp;
last = idx + 1;
while(idx >= 0);
cur[prop] = data[p];
return result[""];
JSON.flatten = function(data)
var result = ;
function recurse (cur, prop)
if (Object(cur) !== cur)
result[prop] = cur;
else if (Array.isArray(cur))
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop ? prop+"."+i : ""+i);
if (l == 0)
result[prop] = [];
else
var isEmpty = true;
for (var p in cur)
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
if (isEmpty)
result[prop] = ;
recurse(data, "");
return result;
EDIT 1 将以上内容修改为@Bergi 的实现,这是目前最快的。顺便说一句,使用“.indexOf”而不是“regex.exec”在 FF 中快 20% 左右,但在 Chrome 中慢 20%;所以我会坚持使用正则表达式,因为它更简单(这是我尝试使用 indexOf 替换正则表达式 http://jsfiddle.net/WSzec/2/)。
编辑 2 基于 @Bergi 的想法,我设法创建了一个更快的非正则表达式版本(FF 中快 3 倍,Chrome 中快约 10%)。 http://jsfiddle.net/WSzec/6/ 在这个(当前)实现中,键名的规则很简单,键不能以整数开头或包含句点。
例子:
"foo":"bar":[0] => "foo.bar.0":0EDIT 3 添加@AaditMShah 的内联路径解析方法(而不是String.split)有助于提高非扁平化性能。我对所达到的整体性能提升感到非常满意。
最新的jsfiddle和jsperf:
http://jsfiddle.net/WSzec/14/
http://jsperf.com/flatten-un-flatten/4
【问题讨论】:
There is no such thing as a "JSON object"。问题似乎与 JS 对象有关。 这个问题似乎更适合 Code Review StackExchange 站点:codereview.stackexchange.com @FelixKling - JSON 对象是指仅包含原始 JavaScript 类型的 JS 对象。例如,您可以在 JS 对象中放置一个函数,但它不会被序列化为 JSON——即 JSON.stringify(fn:function()alert('a');); --[1].[1].[0]
在我看来是错误的。你确定这是想要的结果吗?
不幸的是,有一个错误:日期对象被转换为空的 JSON。
【参考方案1】:
这是我的更短的实现:
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;
;
flatten
没有太大变化(我不确定你是否真的需要那些 isEmpty
案例):
Object.flatten = function(data)
var result = ;
function recurse (cur, prop)
if (Object(cur) !== cur)
result[prop] = cur;
else if (Array.isArray(cur))
for(var i=0, l=cur.length; i<l; i++)
recurse(cur[i], prop + "[" + i + "]");
if (l == 0)
result[prop] = [];
else
var isEmpty = true;
for (var p in cur)
isEmpty = false;
recurse(cur[p], prop ? prop+"."+p : p);
if (isEmpty && prop)
result[prop] = ;
recurse(data, "");
return result;
他们在一起run your benchmark 大约有一半的时间(Opera 12.16:~900ms 而不是~1900ms,Chrome 29:~800ms 而不是~1600ms)。
注意:此解决方案和此处回答的大多数其他解决方案都侧重于速度,并且容易受到prototype pollution 的影响,并且不应用于不受信任的对象。
【讨论】:
这太棒了!正则表达式运行得非常好(尤其是在 Chrome 中),我尝试用 indexOf 逻辑替换它,但只能在 FF 中实现加速。我将在这个问题上增加一个赏金,看看是否可以激发另一个聪明的改进,但到目前为止,这比我希望的要多。 我设法通过用 string.split() 替换 regex.exec() 并简化密钥格式来提高您的实施速度。我会在我给你分之前给它几天,但我认为“有意义的优化之墙”已经达到了。 JSON.flatten(); // '': -- 你可以在 var result = ; 之后添加一行-- if (result === data) 返回数据; @Ivan:啊,感谢那个边缘情况,尽管从语义上讲它实际上需要为空对象提供额外的表示。但是不,result === data
不起作用,它们永远不会完全相同。
@Bergi 是的,你是对的。 Object.keys(data).length === 0 有效【参考方案2】:
这是另一种运行速度比上述答案慢(大约 1000 毫秒)的方法,但有一个有趣的想法 :-)
它不是遍历每个属性链,而是只选择最后一个属性并使用查找表来存储其余的属性。这个查找表将被迭代,直到没有剩余的属性链并且所有值都驻留在未连接的属性上。
JSON.unflatten = function(data)
"use strict";
if (Object(data) !== data || Array.isArray(data))
return data;
var regex = /\.?([^.\[\]]+)$|\[(\d+)\]$/,
props = Object.keys(data),
result, p;
while(p = props.shift())
var m = regex.exec(p),
target;
if (m.index)
var rest = p.slice(0, m.index);
if (!(rest in data))
data[rest] = m[2] ? [] : ;
props.push(rest);
target = data[rest];
else
target = result || (result = (m[2] ? [] : ));
target[m[2] || m[1]] = data[p];
return result;
;
它目前使用data
表格的输入参数,并在其上放置了许多属性——非破坏性版本也应该是可能的。也许聪明的lastIndexOf
用法比正则表达式表现更好(取决于正则表达式引擎)。
See it in action here.
【讨论】:
我没有否决您的回答。但是我想指出,您的函数没有正确地unflatten
扁平化对象。例如考虑数组[1,[2,[3,4],5],6]
。您的flatten
函数将此对象展平为"[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6
。但是,您的 unflatten
函数错误地将展平对象展开为 [1,[null,[3,4]],6]
。发生这种情况的原因是因为声明 delete data[p]
在添加 [3,4]
之前过早地删除了中间值 [2,null,5]
。使用堆栈来解决它。 :-)
啊,我明白了,未定义的枚举顺序...要用一组属性来修复它,请将您的堆栈解决方案放入自己的答案中。感谢您的提示!【参考方案3】:
我写了两个函数到flatten
和unflatten
一个JSON 对象。
Flatten a JSON object:
var flatten = (function (isArray, wrapped)
return function (table)
return reduce("", , table);
;
function reduce(path, accumulator, table)
if (isArray(table))
var length = table.length;
if (length)
var index = 0;
while (index < length)
var property = path + "[" + index + "]", item = table[index++];
if (wrapped(item) !== item) accumulator[property] = item;
else reduce(property, accumulator, item);
else accumulator[path] = table;
else
var empty = true;
if (path)
for (var property in table)
var item = table[property], property = path + "." + property, empty = false;
if (wrapped(item) !== item) accumulator[property] = item;
else reduce(property, accumulator, item);
else
for (var property in table)
var item = table[property], empty = false;
if (wrapped(item) !== item) accumulator[property] = item;
else reduce(property, accumulator, item);
if (empty) accumulator[path] = table;
return accumulator;
(Array.isArray, Object));
性能:
-
它比 Opera 中的当前解决方案更快。目前的解决方案在 Opera 中慢了 26%。
它比 Firefox 中的当前解决方案更快。目前的解决方案在 Firefox 中慢了 9%。
它比 Chrome 中的当前解决方案更快。目前的解决方案在 Chrome 中慢了 29%。
Unflatten a JSON object:
function unflatten(table)
var result = ;
for (var path in table)
var cursor = result, length = path.length, property = "", index = 0;
while (index < length)
var char = path.charAt(index);
if (char === "[")
var start = index + 1,
end = path.indexOf("]", start),
cursor = cursor[property] = cursor[property] || [],
property = path.slice(start, end),
index = end + 1;
else
var cursor = cursor[property] = cursor[property] || ,
start = char === "." ? index + 1 : index,
bracket = path.indexOf("[", start),
dot = path.indexOf(".", start);
if (bracket < 0 && dot < 0) var end = index = length;
else if (bracket < 0) var end = index = dot;
else if (dot < 0) var end = index = bracket;
else var end = index = bracket < dot ? bracket : dot;
var property = path.slice(start, end);
cursor[property] = table[path];
return result[""];
性能:
-
它比 Opera 中的当前解决方案更快。当前的解决方案在 Opera 中慢了 5%。
它比 Firefox 中的当前解决方案慢。我的解决方案在 Firefox 中慢了 26%。
它比 Chrome 中的当前解决方案慢。我的解决方案在 Chrome 中慢了 6%。
Flatten and unflatten a JSON object:
总体而言,我的解决方案的性能与当前解决方案相同甚至更好。
性能:
-
它比 Opera 中的当前解决方案更快。当前的解决方案在 Opera 中慢了 21%。
它与 Firefox 中的当前解决方案一样快。
它比 Firefox 中的当前解决方案更快。当前的解决方案在 Chrome 中慢了 20%。
输出格式:
扁平化对象对对象属性使用点表示法,对数组索引使用方括号表示法:
foo:bar:false => "foo.bar":false
a:[b:["c","d"]] => "a[0].b[0]":"c","a[0].b[1]":"d"
[1,[2,[3,4],5],6] => "[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6
在我看来,这种格式比只使用点表示法更好:
foo:bar:false => "foo.bar":false
a:[b:["c","d"]] => "a.0.b.0":"c","a.0.b.1":"d"
[1,[2,[3,4],5],6] => "0":1,"1.0":2,"1.1.0":3,"1.1.1":4,"1.2":5,"2":6
优势:
-
展平对象比当前解决方案更快。
展平和取消展平对象的速度与当前解决方案一样快,甚至更快。
为了便于阅读,展平对象同时使用点表示法和方括号表示法。
缺点:
-
在大多数(但不是全部)情况下,展开对象比当前解决方案慢。
当前的JSFiddle demo 给出了以下值作为输出:
Nested : 132175 : 63
Flattened : 132175 : 564
Nested : 132175 : 54
Flattened : 132175 : 508
我更新后的JSFiddle demo 给出了以下值作为输出:
Nested : 132175 : 59
Flattened : 132175 : 514
Nested : 132175 : 60
Flattened : 132175 : 451
我不太确定这意味着什么,所以我会坚持使用 jsPerf 结果。毕竟 jsPerf 是一个性能基准测试工具。 JSFiddle 不是。
【讨论】:
非常酷。我真的很喜欢 flatten 的风格,使用匿名函数将 Array.isArray 和 Object 放到更近的范围内。我认为您用于 JSPerf 测试的测试对象太简单了。我在我的 jsfiddle 基准测试中创建了对象“fillObj(,4)”,以模拟大型复杂嵌套数据的真实案例。 向我展示您的对象的代码,我会将其合并到基准测试中。 @LastCoder 嗯,在大多数浏览器(尤其是 Firefox)中,您当前的实现似乎比我的要快。有趣的是,我在 Opera 中的实现速度更快,在 Chrome 中也没有那么糟糕。我不认为拥有这么大的数据集是决定算法速度的理想因素,因为:1)大数据集需要大量的内存、页面交换等;这不是您可以在 JS 中控制的东西(即您受浏览器的支配)2)如果您想做 CPU 密集型工作,那么 JS 不是最好的语言。考虑改用 C。 C 有 JSON 库 这是一个很好的观点,并提出了合成与现实世界基准测试之间的差异。我对当前优化的 JS 的性能感到满意,因此无需使用 C。 这个实现还有一个原型污染错误,例如unflatten("foo.__proto__.bar": 42)
【参考方案4】:
此代码递归地展平 JSON 对象。
我在代码中包含了我的计时机制,它给了我 1 毫秒,但我不确定这是否是最准确的。
var new_json = [
"name": "fatima",
"age": 25,
"neighbour":
"name": "taqi",
"location": "end of the street",
"property":
"built in": 1990,
"owned": false,
"years on market": [1990, 1998, 2002, 2013],
"year short listed": [], //means never
,
"town": "Mountain View",
"state": "CA"
,
"name": "qianru",
"age": 20,
"neighbour":
"name": "joe",
"location": "opposite to the park",
"property":
"built in": 2011,
"owned": true,
"years on market": [1996, 2011],
"year short listed": [], //means never
,
"town": "Pittsburgh",
"state": "PA"
]
function flatten(json, flattened, str_key)
for (var key in json)
if (json.hasOwnProperty(key))
if (json[key] instanceof Object && json[key] != "")
flatten(json[key], flattened, str_key + "." + key);
else
flattened[str_key + "." + key] = json[key];
var flattened = ;
console.time('flatten');
flatten(new_json, flattened, "");
console.timeEnd('flatten');
for (var key in flattened)
console.log(key + ": " + flattened[key]);
输出:
flatten: 1ms
.0.name: fatima
.0.age: 25
.0.neighbour.name: taqi
.0.neighbour.location: end of the street
.0.neighbour.property.built in: 1990
.0.neighbour.property.owned: false
.0.neighbour.property.years on market.0: 1990
.0.neighbour.property.years on market.1: 1998
.0.neighbour.property.years on market.2: 2002
.0.neighbour.property.years on market.3: 2013
.0.neighbour.property.year short listed:
.0.town: Mountain View
.0.state: CA
.1.name: qianru
.1.age: 20
.1.neighbour.name: joe
.1.neighbour.location: opposite to the park
.1.neighbour.property.built in: 2011
.1.neighbour.property.owned: true
.1.neighbour.property.years on market.0: 1996
.1.neighbour.property.years on market.1: 2011
.1.neighbour.property.year short listed:
.1.town: Pittsburgh
.1.state: PA
【讨论】:
我认为,typeof some === 'object'
比 some instanceof Object
更快,因为第一次检查在 O1 中执行,而第二次在 On 中执行,其中 n 是继承链的长度(对象将始终是那里的最后一个)。【参考方案5】:
我通过次要代码重构和将递归函数移到函数命名空间之外,为所选答案增加了 +/- 10-15% 的效率。
请参阅我的问题:Are namespaced functions reevaluated on every call?,了解为什么这会减慢嵌套函数的速度。
function _flatten (target, obj, path)
var i, empty;
if (obj.constructor === Object)
empty = true;
for (i in obj)
empty = false;
_flatten(target, obj[i], path ? path + '.' + i : i);
if (empty && path)
target[path] = ;
else if (obj.constructor === Array)
i = obj.length;
if (i > 0)
while (i--)
_flatten(target, obj[i], path + '[' + i + ']');
else
target[path] = [];
else
target[path] = obj;
function flatten (data)
var result = ;
_flatten(result, data, null);
return result;
见benchmark。
【讨论】:
【参考方案6】:我想添加一个新版本的 flatten case(这是我需要的 :)),根据我对上述 jsFiddler 的探测,它比当前选择的要快一些。 此外,我个人认为这个 sn-p 更具可读性,这对于多开发者项目当然很重要。
function flattenObject(graph)
let result = ,
item,
key;
function recurr(graph, path)
if (Array.isArray(graph))
graph.forEach(function (itm, idx)
key = path + '[' + idx + ']';
if (itm && typeof itm === 'object')
recurr(itm, key);
else
result[key] = itm;
);
else
Reflect.ownKeys(graph).forEach(function (p)
key = path + '.' + p;
item = graph[p];
if (item && typeof item === 'object')
recurr(item, key);
else
result[key] = item;
);
recurr(graph, '');
return result;
【讨论】:
【参考方案7】:您可以使用https://github.com/hughsk/flat
获取嵌套的 Javascript 对象并将其展平,或使用分隔键解展对象。
文档中的示例
var flatten = require('flat')
flatten(
key1:
keyA: 'valueI'
,
key2:
keyB: 'valueII'
,
key3: a: b: c: 2
)
//
// 'key1.keyA': 'valueI',
// 'key2.keyB': 'valueII',
// 'key3.a.b.c': 2
//
var unflatten = require('flat').unflatten
unflatten(
'three.levels.deep': 42,
'three.levels':
nested: true
)
//
// three:
// levels:
// deep: 42,
// nested: true
//
//
//
【讨论】:
你如何在 AngularJS 中使用它?【参考方案8】:这是我的。它在 Google Apps 脚本中在一个相当大的对象上运行时间小于 2 毫秒。它使用破折号而不是点作为分隔符,并且它不像提问者的问题那样处理数组,但这是我想要使用的。
function flatten (obj)
var newObj = ;
for (var key in obj)
if (typeof obj[key] === 'object' && obj[key] !== null)
var temp = flatten(obj[key])
for (var key2 in temp)
newObj[key+"-"+key2] = temp[key2];
else
newObj[key] = obj[key];
return newObj;
例子:
var test =
a: 1,
b: 2,
c:
c1: 3.1,
c2: 3.2
,
d: 4,
e:
e1: 5.1,
e2: 5.2,
e3:
e3a: 5.31,
e3b: 5.32
,
e4: 5.4
,
f: 6
Logger.log("start");
Logger.log(JSON.stringify(flatten(test),null,2));
Logger.log("done");
示例输出:
[17-02-08 13:21:05:245 CST] start
[17-02-08 13:21:05:246 CST]
"a": 1,
"b": 2,
"c-c1": 3.1,
"c-c2": 3.2,
"d": 4,
"e-e1": 5.1,
"e-e2": 5.2,
"e-e3-e3a": 5.31,
"e-e3-e3b": 5.32,
"e-e4": 5.4,
"f": 6
[17-02-08 13:21:05:247 CST] done
【讨论】:
【参考方案9】:3 ½ 年后...
对于我自己的项目,我想将 mongoDB dot notation 中的 JSON 对象展平,并提出了一个简单的解决方案:
/**
* Recursively flattens a JSON object using dot notation.
*
* NOTE: input must be an object as described by JSON spec. Arbitrary
* JS objects (e.g. a: () => 42) may result in unexpected output.
* MOREOVER, it removes keys with empty objects/arrays as value (see
* examples bellow).
*
* @example
* // returns a:1, 'b.0.c': 2, 'b.0.d.e': 3, 'b.1': 4
* flatten(a: 1, b: [c: 2, d: e: 3, 4])
* // returns a:1, 'b.0.c': 2, 'b.0.d.e.0': true, 'b.0.d.e.1': false, 'b.0.d.e.2.f': 1
* flatten(a: 1, b: [c: 2, d: e: [true, false, f: 1]])
* // return a: 1
* flatten(a: 1, b: [], c: )
*
* @param obj item to be flattened
* @param Array.string [prefix=[]] chain of prefix joined with a dot and prepended to key
* @param Object [current=] result of flatten during the recursion
*
* @see https://docs.mongodb.com/manual/core/document/#dot-notation
*/
function flatten (obj, prefix, current)
prefix = prefix || []
current = current ||
// Remember kids, null is also an object!
if (typeof (obj) === 'object' && obj !== null)
Object.keys(obj).forEach(key =>
this.flatten(obj[key], prefix.concat(key), current)
)
else
current[prefix.join('.')] = obj
return current
功能和/或注意事项
它只接受 JSON 对象。因此,如果您传递a: () =>
之类的内容,您可能无法得到您想要的!
它会删除空数组和对象。所以这个a: , b: []
被扁平化为
。
【讨论】:
很好,但我不处理转义的引号。所以"x": "abc\"x\"yz"
变成 "x": "abc","x","yz"
这是无效的。【参考方案10】:
ES6 版本:
const flatten = (obj, path = '') =>
if (!(obj instanceof Object)) return [path.replace(/\.$/g, '')]:obj;
return Object.keys(obj).reduce((output, key) =>
return obj instanceof Array ?
...output, ...flatten(obj[key], path + '[' + key + '].'):
...output, ...flatten(obj[key], path + key + '.');
, );
例子:
console.log(flatten(a:[b:["c","d"]]));
console.log(flatten([1,[2,[3,4],5],6]));
【讨论】:
我认为如果你在属性名称之间没有分隔符 JSON.stringify(flatten("prop1":0,"prop2":" prop3":true,"prop4":"test")); ==> "prop1":0,"prop2prop3":true,"prop2prop4":"test" i> 但是很容易解决,ES6 语法的简洁性非常好 这不能很好地与Date
配合使用,知道如何让它做到这一点吗?例如,flatten(a: b: new Date());
您可以使用时间戳:b: new Date().getTime() 并稍后使用新日期(时间戳)将其返回到日期
这太慢了。【参考方案11】:
使用这个库:
npm install flat
用法(来自https://www.npmjs.com/package/flat):
展平:
var flatten = require('flat')
flatten(
key1:
keyA: 'valueI'
,
key2:
keyB: 'valueII'
,
key3: a: b: c: 2
)
//
// 'key1.keyA': 'valueI',
// 'key2.keyB': 'valueII',
// 'key3.a.b.c': 2
//
未展平:
var unflatten = require('flat').unflatten
unflatten(
'three.levels.deep': 42,
'three.levels':
nested: true
)
//
// three:
// levels:
// deep: 42,
// nested: true
//
//
//
【讨论】:
要完成您的答案,您应该添加一个如何使用该库的示例。 似乎是一个合法的模块。 NPM 每周下载量:3,812,119 (!!!) 并定期更新。感谢您的提醒。【参考方案12】:这是我编写的一些代码,用于展平我正在使用的对象。它创建了一个新类,该类接受每个嵌套字段并将其带入第一层。您可以通过记住键的原始位置来修改它以展开。它还假设键是唯一的,即使在嵌套对象中也是如此。希望对您有所帮助。
class JSONFlattener
ojson =
flattenedjson =
constructor(original_json)
this.ojson = original_json
this.flattenedjson =
this.flatten()
flatten()
Object.keys(this.ojson).forEach(function(key)
if (this.ojson[key] == null)
else if (this.ojson[key].constructor == ().constructor)
this.combine(new JSONFlattener(this.ojson[key]).returnJSON())
else
this.flattenedjson[key] = this.ojson[key]
, this)
combine(new_json)
//assumes new_json is a flat array
Object.keys(new_json).forEach(function(key)
if (!this.flattenedjson.hasOwnProperty(key))
this.flattenedjson[key] = new_json[key]
else
console.log(key+" is a duplicate key")
, this)
returnJSON()
return this.flattenedjson
console.log(new JSONFlattener(dad_dictionary).returnJSON())
例如,它转换
nested_json =
"a":
"b":
"c":
"d":
"a": 0
,
"z":
"b":1
,
"d":
"c":
"c": 2
进入
a: 0, b: 1, c: 2
【讨论】:
【参考方案13】:这是我在 PowerShell 中拼合的一个递归解决方案:
#---helper function for ConvertTo-JhcUtilJsonTable
#
function getNodes
param (
[Parameter(Mandatory)]
[System.Object]
$job,
[Parameter(Mandatory)]
[System.String]
$path
)
$t = $job.GetType()
$ct = 0
$h = @
if ($t.Name -eq 'PSCustomObject')
foreach ($m in Get-Member -InputObject $job -MemberType NoteProperty)
getNodes -job $job.($m.Name) -path ($path + '.' + $m.Name)
elseif ($t.Name -eq 'Object[]')
foreach ($o in $job)
getNodes -job $o -path ($path + "[$ct]")
$ct++
else
$h[$path] = $job
$h
#---flattens a JSON document object into a key value table where keys are proper JSON paths corresponding to their value
#
function ConvertTo-JhcUtilJsonTable
param (
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[System.Object[]]
$jsonObj
)
begin
$rootNode = 'root'
process
foreach ($o in $jsonObj)
$table = getNodes -job $o -path $rootNode
# $h = @
$a = @()
$pat = '^' + $rootNode
foreach ($i in $table)
foreach ($k in $i.keys)
# $h[$k -replace $pat, ''] = $i[$k]
$a += New-Object -TypeName psobject -Property @'Key' = $($k -replace $pat, ''); 'Value' = $i[$k]
# $h[$k -replace $pat, ''] = $i[$k]
# $h
$a
end
例子:
'"name": "John","Address": "house": "1234", "Street": "Boogie Ave", "pets": ["Type": "Dog", "Age": 4, "Toys": ["rubberBall", "rope"],"Type": "Cat", "Age": 7, "Toys": ["catNip"]]' | ConvertFrom-Json | ConvertTo-JhcUtilJsonTable
Key Value
--- -----
.Address.house 1234
.Address.Street Boogie Ave
.name John
.pets[0].Age 4
.pets[0].Toys[0] rubberBall
.pets[0].Toys[1] rope
.pets[0].Type Dog
.pets[1].Age 7
.pets[1].Toys[0] catNip
.pets[1].Type Cat
【讨论】:
【参考方案14】:你可以试试 jpflat 包。
它扁平化、膨胀、解析承诺、扁平化数组,具有可自定义的路径创建和可自定义的值序列化。
reducers 和 serializers 将整个路径作为其部分的数组接收,因此可以对路径执行更复杂的操作,而不是修改单个键或更改分隔符。
Json 路径是默认的,因此是 "jp"flat。
https://www.npmjs.com/package/jpflat
let flatFoo = await require('jpflat').flatten(foo)
【讨论】:
【参考方案15】:我想要一种方法,以便能够轻松地将我的 json 数据转换为 csv 文件。 场景是:我从某个地方查询数据,我收到一些模型的数组,比如银行提取物。 下面的这种方法用于解析这些条目中的每一个。
function jsonFlatter(data, previousKey, obj)
obj = obj ||
previousKey = previousKey || ""
Object.keys(data).map(key =>
let newKey = `$previousKey$previousKey ? "_" : ""$key`
let _value = data[key]
let isArray = Array.isArray(_value)
if (typeof _value !== "object" || isArray || _value == null)
if (isArray)
_value = JSON.stringify(_value)
else if (_value == null)
_value = "null"
obj[newKey] = _value
else if (typeof _value === "object")
if (!Object.keys(_value).length)
obj[newKey] = "null"
else
return jsonFlatter(_value, newKey, obj)
)
return obj
这样,我可以依靠对象模型的键和内部键的一致性,但是数组只是被字符串化了,因为我不能依赖它们的一致性。此外,空对象成为字符串“null”,因为我仍然希望它是关键出现在最终结果中。
使用示例:
const test_data =
a:
aa:
aaa: 4354,
aab: 654
,
ab: 123
,
b: 234,
c: ,
d: []
console.log('result', jsonFlatter(test_data))
#### output
"a_aa_aaa": 4354,
"a_aa_aab": 654,
"a_ab": 123,
"b": 234,
"c": "null",
"d": "[]"
【讨论】:
【参考方案16】:Object.prototype.flatten = function (obj)
let ans = ;
let anotherObj = ...obj ;
function performFlatten(anotherObj)
Object.keys(anotherObj).forEach((key, idx) =>
if (typeof anotherObj[key] !== 'object')
ans[key] = anotherObj[key];
console.log('ans so far : ', ans);
else
console.log(key, ...anotherObj[key] );
performFlatten(anotherObj[key]);
)
performFlatten(anotherObj);
return ans;
let ans = flatten(obj);
console.log(ans);
【讨论】:
【参考方案17】:试试这个:
function getFlattenObject(data, response = )
for (const key in data)
if (typeof data[key] === 'object' && !Array.isArray(data[key]))
getFlattenObject(data[key], response);
else
response[key] = data[key];
return response;
【讨论】:
以上是关于展平/取消展平嵌套 JSON 对象的最快方法的主要内容,如果未能解决你的问题,请参考以下文章
使用 Azure Synapse pyspark 过滤器根据嵌套对象的数据类型展平嵌套的 json 对象