2014-10-08 119 views
1

我想将JavaScript对象序列化为JSON格式并将其反序列化。检测JavaScript对象是否具有引用DOM元素的某些属性

明显的解决方案是使用JSON.stringify()。重点在于关于JSON.stringify()的主要问题是它在尝试序列化循环对象时返回错误。返回的错误是以下

TypeError: Converting circular structure to JSON 

一些建议的解决方案就像cycle.jscircular.js可以使一个解决办法,以便能够序列化和反序列化循环对象。

这些解决方案的一个问题是,它们不允许序列化引用DOM元素的循环对象或包含指向DOM元素的属性的最差循环对象。例如,使用cycle.js返回

Failed to read the 'selectionDirection' property from 'HTMLInputElement': The input element's type ('image') does not support selection. 

我考虑检测到使用document.contains(objName)当对象必须在DOM树中的现有元素的引用,它返回真DOM元素的引用的存在。如果我能够检测到这些元素,我会标记这些引用并将其删除,以便使用cycle.js可以序列化一个新对象,并在反序列化后将它们重新指向DOM元素。

我的问题是,我不提前知道对象是否有指向DOM元素的属性,当我想要解析所有属性和属性reccursivly时,我不会能够停止解析,因为对象可以是循环的(原来的问题),我会收到以下错误

Maximum call stack size exceeded 

任何线索?

+0

你能提供你试图记住在json中的对象吗? (jsfiddle.net) – zoran404 2014-10-08 14:28:26

+0

我在jsfiddle.net上尝试了我的整个脚本,它在firefox和chrome之间的行为不同,在jsfiddle上使用它并将它用作本地主机上的文件的方式不同。 这里是我在jsfiddle.net的例子[链接](http://jsfiddle.net/u3yygkwt/6/)。我请求你在本地主机上尝试它。我的对象是: 'var obj = new Object(); obj.x = 1; obj.y = 2; obj.z = obj; obj.div = document.getElementById('div');'其中div可以是任何DOM元素 – Bourados 2014-10-08 15:34:31

+0

我更新了[script](http://jsfiddle.net/u3yygkwt/8/)中的警报,使其更具表现力 – Bourados 2014-10-08 15:43:08

回答

0

我发现了上述问题的解决方案:

我所做的是我添加2线cycle.js标记指向DOM元素的对象和为了不使它们的属性里面寻找打破他们。我强调了所添加的行cycle.js内:

/* 
    cycle.js 
    2013-02-19 

    Public Domain. 

    NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. 

    This code should be minified before deployment. 
    See http://javascript.crockford.com/jsmin.html 

    USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO 
    NOT CONTROL. 
*/ 

/*jslint evil: true, regexp: true */ 

/*members $ref, apply, call, decycle, hasOwnProperty, length, prototype, push, 
    retrocycle, stringify, test, toString 
*/ 

if (typeof JSON.decycle !== 'function') { 
    JSON.decycle = function decycle(object) { 
     'use strict'; 

// Make a deep copy of an object or array, assuring that there is at most 
// one instance of each object or array in the resulting structure. The 
// duplicate references (which might be forming cycles) are replaced with 
// an object of the form 
//  {$ref: PATH} 
// where the PATH is a JSONPath string that locates the first occurance. 
// So, 
//  var a = []; 
//  a[0] = a; 
//  return JSON.stringify(JSON.decycle(a)); 
// produces the string '[{"$ref":"$"}]'. 

// JSONPath is used to locate the unique object. $ indicates the top level of 
// the object or array. [NUMBER] or [STRING] indicates a child member or 
// property. 

     var objects = [], // Keep a reference to each unique object or array 
      paths = [];  // Keep the path to each unique object or array 

     return (function derez(value, path) { 

// The derez recurses through the object, producing the deep copy. 

      var i,   // The loop counter 
       name,  // Property name 
       nu;   // The new object or array 

// typeof null === 'object', so go on if this value is really an object but not 
// one of the weird builtin objects. 

      if (typeof value === 'object' && value !== null && 
        !(value instanceof Boolean) && 
        !(value instanceof Date) && 
        !(value instanceof Number) && 
        !(value instanceof RegExp) && 
        !(value instanceof String)) { 

// If the value is an object or array, look to see if we have already 
// encountered it. If so, return a $ref/path object. This is a hard way, 
// linear search that will get slower as the number of unique objects grows. 

/* ************************************************************* */ 
/* ******************** START OF ADDED CODE ******************** */ 
/* ************************************************************* */ 
       if (document.contains(value)) 
        return {$ref: "TODO mark the position of elements in DOM tree"}; 
/* ************************************************************* */ 
/* ********************* END OF ADDED CODE ********************* */    
/* ************************************************************* */ 

       for (i = 0; i < objects.length; i += 1) { 
        if (objects[i] === value) { 
         return {$ref: paths[i]}; 
        } 
       } 

// Otherwise, accumulate the unique value and its path. 

       objects.push(value); 
       paths.push(path); 

// If it is an array, replicate the array. 

       if (Object.prototype.toString.apply(value) === '[object Array]') { 
        nu = []; 
        for (i = 0; i < value.length; i += 1) { 
         nu[i] = derez(value[i], path + '[' + i + ']'); 
        } 
       } else { 

// If it is an object, replicate the object. 

        nu = {}; 
        for (name in value) { 
         if (Object.prototype.hasOwnProperty.call(value, name)) { 
          nu[name] = derez(value[name], 
           path + '[' + JSON.stringify(name) + ']'); 
         } 
        } 
       } 
       return nu; 
      } 
      return value; 
     }(object, '$')); 
    }; 
} 


if (typeof JSON.retrocycle !== 'function') { 
    JSON.retrocycle = function retrocycle($) { 
     'use strict'; 

// Restore an object that was reduced by decycle. Members whose values are 
// objects of the form 
//  {$ref: PATH} 
// are replaced with references to the value found by the PATH. This will 
// restore cycles. The object will be mutated. 

// The eval function is used to locate the values described by a PATH. The 
// root object is kept in a $ variable. A regular expression is used to 
// assure that the PATH is extremely well formed. The regexp contains nested 
// * quantifiers. That has been known to have extremely bad performance 
// problems on some browsers for very long strings. A PATH is expected to be 
// reasonably short. A PATH is allowed to belong to a very restricted subset of 
// Goessner's JSONPath. 

// So, 
//  var s = '[{"$ref":"$"}]'; 
//  return JSON.retrocycle(JSON.parse(s)); 
// produces an array containing a single element which is the array itself. 

     var px = 
      /^\$(?:\[(?:\d+|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/; 

     (function rez(value) { 

// The rez function walks recursively through the object looking for $ref 
// properties. When it finds one that has a value that is a path, then it 
// replaces the $ref object with a reference to the value that is found by 
// the path. 

      var i, item, name, path; 

      if (value && typeof value === 'object') { 
       if (Object.prototype.toString.apply(value) === '[object Array]') { 
        for (i = 0; i < value.length; i += 1) { 
         item = value[i]; 
         if (item && typeof item === 'object') { 
          path = item.$ref; 
          if (typeof path === 'string' && px.test(path)) { 
           value[i] = eval(path); 
          } else { 
           rez(item); 
          } 
         } 
        } 
       } else { 
        for (name in value) { 
         if (typeof value[name] === 'object') { 
          item = value[name]; 
          if (item) { 
           path = item.$ref; 
           if (typeof path === 'string' && px.test(path)) { 
            value[name] = eval(path); 
           } else { 
            rez(item); 
           } 
          } 
         } 
        } 
       } 
      } 
     }($)); 
     return $; 
    }; 
} 

我添加了一个example到jsfiddle.net(试穿上的jsfiddle或本地,火狐32.0.3工作不正常镀铬)。下一步是找到一种方法来检测DOM元素的位置,以便在序列化对象之后重新建立引用,并在我们没有ID时反序列化它。

+0

我认为使用“value instanceof HTMLElement”而不是“document.contains(value)”会更实用。至于保存html元素,你可以保存它的id(或者为它分配一个新的id,如果它没有id)以供将来参考。 – zoran404 2014-10-09 09:13:16

+0

是的,使用'value instanceof HTMLElement'更实用。 – Bourados 2014-10-09 10:00:20

+0

我会至少使用身份证:) – Bourados 2014-10-09 10:01:06

相关问题