Đối tượng tuần tự có chứa giá trị đối tượng tuần hoàn


151

Tôi có một đối tượng (cây phân tích cú pháp) có chứa các nút con được tham chiếu đến các nút khác.

Tôi muốn tuần tự hóa đối tượng này, bằng cách sử dụng JSON.stringify(), nhưng tôi nhận được

TypeError: giá trị đối tượng tuần hoàn

bởi vì các cấu trúc tôi đã đề cập.

Làm thế nào tôi có thể làm việc xung quanh này? Nó không quan trọng với tôi cho dù các tham chiếu đến các nút khác được đại diện hay không trong đối tượng được tuần tự hóa.

Mặt khác, việc loại bỏ các thuộc tính này khỏi đối tượng khi chúng được tạo có vẻ tẻ nhạt và tôi sẽ không muốn thay đổi trình phân tích cú pháp (narcissus).


1
Chúng tôi không thể giúp bạn mà không có một số mã. Vui lòng gửi các bit có liên quan của đối tượng của bạn và / hoặc đầu ra JSON cùng với JS bạn sử dụng để tuần tự hóa nó.
Bojangles

1
Bạn có thể thêm một số tiền tố vào các thuộc tính đó là các tham chiếu nội bộ không?
theo đó

@Loic Sẽ rất có giá trị nếu có cycle.jscâu trả lời của Douglas Crockford ở đây, vì đó là giải pháp phù hợp nhất cho rất nhiều trường hợp. Có vẻ thích hợp để bạn đăng câu trả lời đó, vì bạn là người đầu tiên tham khảo nó (trong bình luận của bạn dưới đây). Nếu bạn không cảm thấy muốn đăng nó như một câu trả lời, cuối cùng tôi sẽ làm như vậy.
Jeremy Banks


1
Tôi ước JSON sẽ thông minh hơn hoặc cách giải quyết vấn đề này dễ dàng hơn. Các giải pháp quá rắc rối cho mục đích gỡ lỗi đơn giản (!) Imo.
BluE

Câu trả lời:


220

Sử dụng tham số thứ hai của stringify, hàm thay thế , để loại trừ các đối tượng đã được tuần tự hóa:

var seen = [];

JSON.stringify(obj, function(key, val) {
   if (val != null && typeof val == "object") {
        if (seen.indexOf(val) >= 0) {
            return;
        }
        seen.push(val);
    }
    return val;
});

http://jsfiddle.net/mH6cJ/38/

Như đã chỉ ra một cách chính xác trong các bình luận khác, mã này sẽ loại bỏ mọi đối tượng "nhìn thấy", không chỉ các đối tượng "đệ quy".

Ví dụ: cho:

a = {x:1};
obj = [a, a];

kết quả sẽ không chính xác. Nếu cấu trúc của bạn là như thế này, bạn có thể muốn sử dụng Crockford decycle hoặc (đơn giản) chức năng này mà chỉ thay thế tài liệu tham khảo đệ quy với null:

function decycle(obj, stack = []) {
    if (!obj || typeof obj !== 'object')
        return obj;
    
    if (stack.includes(obj))
        return null;

    let s = stack.concat([obj]);

    return Array.isArray(obj)
        ? obj.map(x => decycle(x, s))
        : Object.fromEntries(
            Object.entries(obj)
                .map(([k, v]) => [k, decycle(v, s)]));
}

//

let a = {b: [1, 2, 3]}
a.b.push(a);

console.log(JSON.stringify(decycle(a)))


3
aaah tốt đẹp Cảm ơn, tôi sẽ thử cái này. Tôi đã tìm thấy một giải pháp được tạo bởi Douglas Crockford ( github.com/douglascrockford/JSON-js/blob/master/ Motorcycle.js ), nhưng vì tôi không chắc chắn về giấy phép đi kèm với nó, giải pháp dễ dàng mà bạn mô tả sẽ hoàn hảo!
Loic Duros

3
@LoicDuros Giấy phép là "miền công cộng". Có nghĩa là, bạn có thể làm bất cứ điều gì bạn muốn với nó.
Ates Goral

1
mã này tạo ra các vòng lặp đi xe đạp, hãy cẩn thận khi sử dụng, rất có thể làm hỏng ứng dụng của bạn. cần dấu chấm phẩy chính xác và không thể sử dụng trên các đối tượng sự kiện!
Ol Sen

3
Điều này loại bỏ nhiều hơn chỉ là các tham chiếu theo chu kỳ - nó chỉ đơn giản là loại bỏ bất cứ thứ gì xuất hiện nhiều hơn một lần. Trừ khi đối tượng đã được tuần tự hóa là "cha mẹ" của đối tượng mới, bạn không nên xóa nó
Gio

1
Câu trả lời tốt! Tôi đã sửa đổi điều này một chút, thay đổi hàm thành hàm đệ quy, để các đối tượng con sẽ được nhân bản theo cách các đối tượng cha được sao chép.
Hold OfferHunger

2

Tôi đã tạo một GitHub Gist có thể phát hiện các cấu trúc tuần hoàn và cũng giải mã chúng: https://gist.github.com/Hoff97/9842228

Để chuyển đổi, chỉ cần sử dụng JSONE.opesify / JSONE.parse. Nó cũng khử và mã hóa các chức năng. Nếu bạn muốn tắt tính năng này, chỉ cần xóa các dòng 32-48 và 61-85.

var strg = JSONE.stringify(cyclicObject);
var cycObject = JSONE.parse(strg);

Bạn có thể tìm thấy một ví dụ ở đây:

http://jsfiddle.net/hoff97/7UYd4/


2

Đây là một câu trả lời thay thế, nhưng vì nhiều người sẽ đến đây để gỡ lỗi các đối tượng hình tròn của họ và thực sự không phải là một cách tuyệt vời để làm điều đó mà không cần lấy một loạt mã, ở đây đi.

Một tính năng không được biết đến nhiều như JSON.stringify()console.table(). Chỉ cần gọi console.table(whatever);và nó sẽ ghi nhật ký biến trong bảng điều khiển ở định dạng bảng, làm cho nó khá dễ dàng và thuận tiện để kiểm tra nội dung của biến.


1

tiết kiệm hơn nhiều và nó cho thấy một đối tượng chu kỳ ở đâu .

<script>
var jsonify=function(o){
    var seen=[];
    var jso=JSON.stringify(o, function(k,v){
        if (typeof v =='object') {
            if ( !seen.indexOf(v) ) { return '__cycle__'; }
            seen.push(v);
        } return v;
    });
    return jso;
};
var obj={
    g:{
        d:[2,5],
        j:2
    },
    e:10
};
obj.someloopshere = [
    obj.g,
    obj,
    { a: [ obj.e, obj ] }
];
console.log('jsonify=',jsonify(obj));
</script>

sản xuất

jsonify = {"g":{"d":[2,5],"j":2},"e":10,"someloopshere":[{"d":[2,5],"j":2},"__cycle__",{"a":[10,"__cycle__"]}]}

nhưng vẫn còn một vấn đề với mã này nếu ai đó sẽ xây dựng một đối tượng obj.b=this'nếu ai đó biết cách ngăn chặn các calcs rất dài được tạo ra từ một phạm vi nhất định với thissẽ rất tốt để xem ở đây
Ol Sen

2
Điều này nên làseen.indexOf(v) != -1

1

Tôi tạo quá một dự án github có thể tuần tự hóa đối tượng tuần hoàn và khôi phục lớp nếu bạn lưu nó trong thuộc tính serializename như một Chuỗi

var d={}
var a = {b:25,c:6,enfant:d};
d.papa=a;
var b = serializeObjet(a);
assert.equal(  b, "{0:{b:25,c:6,enfant:'tab[1]'},1:{papa:'tab[0]'}}" );
var retCaseDep = parseChaine(b)
assert.equal(  retCaseDep.b, 25 );
assert.equal(  retCaseDep.enfant.papa, retCaseDep );

https://github.com/bormat/serializeStringifyPudeCyclicObject

Chỉnh sửa: Tôi đã chuyển đổi tập lệnh của mình cho NPM https://github.com/bormat/borto_circular_serialize và tôi đã thay đổi tên hàm từ tiếng Pháp sang tiếng Anh.


Ví dụ này không phù hợp với Gist. Gist có lỗi.
Ernst Ernst

Ý tưởng hay - nhưng một khi làm cho nó sẵn sàng :-) Nếu bạn làm cho nó được phân phối trong npm, có thể bạn sẽ phát triển ngay cả các kiểu chữ cho điều đó, nó có thể trở nên khá phổ biến.
peterh - Tái lập Monica

1

Dưới đây là một ví dụ về cấu trúc dữ liệu với các tham chiếu theo chu kỳ: dụng cụ

function makeToolshed(){
    var nut = {name: 'nut'}, bolt = {name: 'bolt'};
    nut.needs = bolt; bolt.needs = nut;
    return { nut: nut, bolt: bolt };
}

Khi bạn muốn GIỮ các tài liệu tham khảo theo chu kỳ (khôi phục chúng khi bạn giải tuần tự hóa, thay vì "gỡ bỏ" chúng), bạn có 2 lựa chọn, tôi sẽ so sánh ở đây. Đầu tiên là Douglas Crockford cycle.js , thứ hai là tôi Siberia gói. Cả hai đều hoạt động bằng cách đầu tiên "giải mã" đối tượng, tức là xây dựng một đối tượng khác (không có bất kỳ tham chiếu theo chu kỳ nào) "chứa cùng một thông tin."

Ông Crockford đi trước:

JSON.decycle(makeToolshed())

JSON_decyclMakeToolshed

Như bạn thấy, cấu trúc lồng nhau của JSON được giữ lại, nhưng có một điều mới, đó là các đối tượng có thuộc tính đặc biệt $ref. Hãy xem cách nó hoạt động.

root = makeToolshed();
[root.bolt === root.nut.needs, root.nut.needs.needs === root.nut]; // retutrns [true,true]

Ký hiệu đô la là viết tắt của gốc. .bolt$refcho chúng tôi biết đó .boltlà một đối tượng "đã thấy" và giá trị của thuộc tính đặc biệt đó (ở đây, chuỗi $ ["nut"] ["nhu cầu"]) cho chúng tôi biết, xem ===ở trên. Tương tự như vậy cho thứ hai $refvà thứ hai ===ở trên.

Chúng ta hãy sử dụng một bài kiểm tra công bằng sâu phù hợp (cụ thể là deepGraphEqualchức năng của Anders Kaseorg từ câu trả lời được chấp nhận cho câu hỏi này ) để xem liệu nhân bản có hoạt động không.

root = makeToolshed();
clone = JSON.retrocycle(JSON.decycle(root));
deepGraphEqual(root, clone) // true
serialized = JSON.stringify(JSON.decycle(root));
clone2 = JSON.retrocycle(JSON.parse(serialized));
deepGraphEqual(root, clone2); // true

Bây giờ, siberia:

JSON.Siberia.forestify(makeToolshed())

JSON_Siberia_forestify_makeToolshed

Siberia không cố bắt chước JSON "cổ điển", không có cấu trúc lồng nhau. Biểu đồ đối tượng được mô tả theo cách "phẳng". Mỗi nút của biểu đồ đối tượng được biến thành một cây phẳng (danh sách cặp giá trị khóa đơn giản với các giá trị chỉ nguyên), là một mục trong .forest.Tại chỉ số 0, chúng tôi tìm thấy đối tượng gốc, ở các chỉ số cao hơn, chúng tôi tìm thấy các nút khác của biểu đồ đối tượng và các giá trị âm (của một số khóa của một số cây trong rừng) trỏ đến atomsmảng, (được gõ thông qua mảng loại, nhưng chúng ta sẽ bỏ qua chi tiết gõ ở đây). Tất cả các nút đầu cuối đều nằm trong bảng nguyên tử, tất cả các nút không đầu cuối đều nằm trong bảng rừng và bạn có thể thấy ngay có bao nhiêu nút mà biểu đồ đối tượng có, cụ thể là forest.length. Hãy kiểm tra nếu nó hoạt động:

root = makeToolshed();
clone = JSON.Siberia.unforestify(JSON.Siberia.forestify(root));
deepGraphEqual(root, clone); // true
serialized = JSON.Siberia.stringify(JSON.Siberia.forestify(root));
clone2 = JSON.Siberia.unforestify(JSON.Siberia.unstringify(serialized));
deepGraphEqual(root, clone2); // true

so sánh

sẽ thêm phần sau.


0
function stringifyObject ( obj ) {
  if ( _.isArray( obj ) || !_.isObject( obj ) ) {
    return obj.toString()
  }
  var seen = [];
  return JSON.stringify(
    obj,
    function( key, val ) {
      if (val != null && typeof val == "object") {
        if ( seen.indexOf( val ) >= 0 )
          return
          seen.push( val )
          }
      return val
    }
  );
}

Một điều kiện tiên quyết đã bị thiếu, nếu không, các giá trị số nguyên trong các đối tượng mảng bị cắt ngắn, tức là [[08.11.2014 12:30:13, 1095]] 1095 bị giảm xuống còn 095.


nhận chuyển hướngError: Không thể tìm thấy biến: _
amit pandya

Vui lòng sửa mã của bạn.
Anastasios Mora viêm
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.