展平/取消展平嵌套 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":0

EDIT 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】:

我写了两个函数到flattenunflatten 一个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 =&gt; "foo.bar":false a:[b:["c","d"]] =&gt; "a[0].b[0]":"c","a[0].b[1]":"d" [1,[2,[3,4],5],6] =&gt; "[0]":1,"[1][0]":2,"[1][1][0]":3,"[1][1][1]":4,"[1][2]":5,"[2]":6

在我看来,这种格式比只使用点表示法更好:

    foo:bar:false =&gt; "foo.bar":false a:[b:["c","d"]] =&gt; "a.0.b.0":"c","a.0.b.1":"d" [1,[2,[3,4],5],6] =&gt; "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: () =&gt; 之类的内容,您可能无法得到您想要的! 它会删除空数组和对象。所以这个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 对象的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

展平嵌套的 JSON 对象

使用 JQ 展平嵌套的 Json 对象

使用 Azure Synapse pyspark 过滤器根据嵌套对象的数据类型展平嵌套的 json 对象

将嵌套的 JSON 对象展平并排序到 javascript 中的数组中

使用唯一键展平/规范化深度嵌套的对象

使用 jq 为 JSON 对象的嵌套数组中的属性展平数组