2014-10-08 120 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

相關問題