javaScript:使用JSON.parse解析字符串化对象会删除引用

时间:2021-12-27 03:54:21

I am trying to deep-clone an object, say "a" with k = JSON.parse(JSON.stringify(a)). It is important that I use the stringify way, since I am trying to save the object into a file and then load from it.

我试图深度克隆一个对象,用k = JSON.parse(JSON.stringify(a))说“a”。我使用stringify方式很重要,因为我试图将对象保存到文件中然后从中加载。

I stumbled upon a problem with references on the cloned object which is illustrated below:

我偶然发现了克隆对象的引用问题,如下所示:

var obj={};
obj.importantProperty={s:2};
obj.c=obj.importantProperty;
obj.d=obj.importantProperty;
console.log( obj.c === obj.d ); // Returns true

var cloned = JSON.parse(JSON.stringify(obj));
console.log( cloned.c === cloned.d ); // Returns false
I need the references to be kept when using JSON.parse, in the above example they are not. In my project the object is much more complicated, but in the end it comes down to the example above.

Thanks in advance to anyone who helps me with this :)

在此先感谢任何帮助我的人:)

2 个解决方案

#1


1  

The proper way to do something like this would be to store the common referenced object(s) separately and reference it by an ID.

这样做的正确方法是分别存储公共引用对象并通过ID引用它。

For instance, you can hold your importantProperty objects in an array and use the index as the ID:

例如,您可以将importantProperty对象保存在数组中,并使用索引作为ID:

var importantProperties = [
  { s: 1 },
  { s: 2 },
  { s: 3 }
];

var obj = {};
obj.importantProperty = importantProperties[1];
obj.c = obj.importantProperty;
obj.d = obj.importantProperty;

Then when you stringify the object you replace the referenced object with its index:

然后,当您对对象进行字符串化时,可以使用其索引替换引用的对象:

var stringified = JSON.stringify(obj, function(key, value) {
  if (key) {
    return importantProperties.indexOf(value);
  }
  return value;
});
console.log(stringified);
// prints {"importantProperty":1,"c":1,"d":1}

And then when you parse you simply reverse the process to revive the references:

然后当你解析时,只需反转该过程即可恢复引用:

var parsed = JSON.parse(stringified, function(key, value) {
  if (key) {
    return importantProperties[value];
  }
  return value;
});
console.log(parsed.c === parsed.d && parsed.d === parsed.importantProperty);
// prints true

Now, the example above works for your example code under the assumption that all properties in obj is an object from the importantProperties array. If that's not the case and it's only certain properties that is an importantProperties object, you need to check for that when replacing/reviving.
Assuming only the "importantProperty", "c" and "d" properties are such objects:
if (['importantProperty', 'c', 'd'].includes(key)) instead of just if (key)

现在,上面的示例适用于您的示例代码,假设obj中的所有属性都是来自importantProperties数组的对象。如果不是这种情况,并且只有某些属性是一个重要的属性对象,则需要在更换/恢复时检查它。假设只有“importantProperty”,“c”和“d”属性是这样的对象:if(['importantProperty','c','d'] .include(key))而不是if(key)

If this isn't good enough and you don't want the property name to have anything to do with whether or not the value is an importantProperties object, you'll need to indicate this in the value together with the identifier. Here's an example of how this can be done:

如果这不够好并且您不希望属性名称与值是否为importantProperties对象有任何关系,则需要在值中与标识符一起指明。以下是如何完成此操作的示例:

// Replacing
JSON.stringify(obj, function(k, value) {
  if (importantProperties.includes(value)) {
    return 'ImportantProperty['
      + importantProperties.indexOf(value)
      + ']';
  }
  return value;
});

// Reviving
JSON.parse(stringified, function(k, value) {
  if (/^ImportantProperty\[\d+\]$/.test(value)) {
    var index = Number( value.match(/\d+/)[0] );
    return importantProperties[index];
  }
  return value;
});

#2


0  

It is impossible to achieve your desired result using JSON because JSON format can contain only a limited ammount of data types (http://json.org/) and when you stringify an object to JSON some information gets lost.

使用JSON无法实现所需的结果,因为JSON格式只能包含有限的数据类型(http://json.org/),当您将对象字符串化为JSON时,某些信息会丢失。

Probably there is some other kind of serialization technique, but I would recommend you to look for another approach to store data.

可能还有一些其他类型的序列化技术,但我建议你寻找另一种存储数据的方法。

#1


1  

The proper way to do something like this would be to store the common referenced object(s) separately and reference it by an ID.

这样做的正确方法是分别存储公共引用对象并通过ID引用它。

For instance, you can hold your importantProperty objects in an array and use the index as the ID:

例如,您可以将importantProperty对象保存在数组中,并使用索引作为ID:

var importantProperties = [
  { s: 1 },
  { s: 2 },
  { s: 3 }
];

var obj = {};
obj.importantProperty = importantProperties[1];
obj.c = obj.importantProperty;
obj.d = obj.importantProperty;

Then when you stringify the object you replace the referenced object with its index:

然后,当您对对象进行字符串化时,可以使用其索引替换引用的对象:

var stringified = JSON.stringify(obj, function(key, value) {
  if (key) {
    return importantProperties.indexOf(value);
  }
  return value;
});
console.log(stringified);
// prints {"importantProperty":1,"c":1,"d":1}

And then when you parse you simply reverse the process to revive the references:

然后当你解析时,只需反转该过程即可恢复引用:

var parsed = JSON.parse(stringified, function(key, value) {
  if (key) {
    return importantProperties[value];
  }
  return value;
});
console.log(parsed.c === parsed.d && parsed.d === parsed.importantProperty);
// prints true

Now, the example above works for your example code under the assumption that all properties in obj is an object from the importantProperties array. If that's not the case and it's only certain properties that is an importantProperties object, you need to check for that when replacing/reviving.
Assuming only the "importantProperty", "c" and "d" properties are such objects:
if (['importantProperty', 'c', 'd'].includes(key)) instead of just if (key)

现在,上面的示例适用于您的示例代码,假设obj中的所有属性都是来自importantProperties数组的对象。如果不是这种情况,并且只有某些属性是一个重要的属性对象,则需要在更换/恢复时检查它。假设只有“importantProperty”,“c”和“d”属性是这样的对象:if(['importantProperty','c','d'] .include(key))而不是if(key)

If this isn't good enough and you don't want the property name to have anything to do with whether or not the value is an importantProperties object, you'll need to indicate this in the value together with the identifier. Here's an example of how this can be done:

如果这不够好并且您不希望属性名称与值是否为importantProperties对象有任何关系,则需要在值中与标识符一起指明。以下是如何完成此操作的示例:

// Replacing
JSON.stringify(obj, function(k, value) {
  if (importantProperties.includes(value)) {
    return 'ImportantProperty['
      + importantProperties.indexOf(value)
      + ']';
  }
  return value;
});

// Reviving
JSON.parse(stringified, function(k, value) {
  if (/^ImportantProperty\[\d+\]$/.test(value)) {
    var index = Number( value.match(/\d+/)[0] );
    return importantProperties[index];
  }
  return value;
});

#2


0  

It is impossible to achieve your desired result using JSON because JSON format can contain only a limited ammount of data types (http://json.org/) and when you stringify an object to JSON some information gets lost.

使用JSON无法实现所需的结果,因为JSON格式只能包含有限的数据类型(http://json.org/),当您将对象字符串化为JSON时,某些信息会丢失。

Probably there is some other kind of serialization technique, but I would recommend you to look for another approach to store data.

可能还有一些其他类型的序列化技术,但我建议你寻找另一种存储数据的方法。