将自定义对象转换为JSON,然后再转换回自定义对象?

7

我看到了很多类似的问题,但我无法确定它们是否被清晰地回答了 - 也许我有点迟钝,抱歉。

我想要拥有自己对象的便捷性(和清晰度),称之为CardboardBox()。 它不会包含代码,只包含数据。 我想将其写入数据库并稍后读取,但显然,当它被读取时,它是类型Object()。 我能想到的唯一方法是找出它曾经是什么:

  1. 有一个成员变量type,我将其设置为CARDBOARD_BOX
  2. 实例化一个新的CarbardBox()并使用一个函数(在盒子中)将Object()的属性复制到新的CardboardBox()对象中

有更好的方法吗? 我相信我可以改变实际类型。

function CardboardBox() { 
  this.type = "CARDBOARD_BOX"
  this.name = "No set";
  this.populate = new function(obj) {
    // populate this object with obj properties 
}

var box = new CarboardBox();  // CarboardBox
box.name = "My Box";
send = JSON.stringyfy(box);   
.
.
.
obj = JSON.parse(send);    // Object

if (obj.type == "CARDBOARD_BOX") {
  savedBox = new CardboardBox();
  savedBox.populate(obj);
}

预先感谢您...史蒂夫

[编辑] 我的测试代码。

function CardboardBox(n) {
  this.name = n;
}

var box = new CardboardBox("My Box");
send = JSON.stringify(box); // JSON CarboardBox()

obj = JSON.parse(send, function fn(obj) { // Object() returned
  log("OB: "+obj.type);
  return obj.type === 'CardboardBox' ? new CardboardBox(obj) : CardboardBox; 
});     
console.log(obj);

输出结果为:

OB: undefined utils.js:40
OB: undefined utils.js:40
function CardboardBox(n) {
    this.name = n;
} 

4
看一下JSON.parse和JSON.stringify的参数。您可以为每个对象设置自定义回调函数。JSON.parse(obj, fn); function fn(obj) { return obj.type === 'a' ? new A(obj) : a; } 如果我有时间,我会写一个正确的答案。抱歉。 - J. K.
谢谢回复。非常感谢,但我现在有点困惑了。如果我在CardboardBox()上使用函数(fn),那么从JSON.parse()返回的是CarboardBox()的构造函数。obj.type始终未定义。我觉得我漏掉了什么...... - Steve
@Steve:你需要设置.type,而且你没有正确使用参数(第一个参数不是建议的obj)。请参见http://jsfiddle.net/LSUJ9/. - pimvdb
为什么你的测试代码没有设置“type”成员?这就是为什么“type”未定义的原因。 - Eric
2个回答

8

可能的解决方案如下:

function CardboardBox(n) {
  if(typeof(n) == 'string') {
    //build from name string
    this.name = n;
  } else {
    //build from object
    this.name = n.name;
  }

  //add in this object's "type" in a place
  //that is unlikely to exist in other JSON strings
  this.__type = 'CardboardBox';
}

var box = new CardboardBox("My Box");
send = JSON.stringify(box), // JSON CarboardBox()
obj = JSON.parse(send, function(key, val) {
  //if this is an object, and is CardboardBox
  if(typeof(val) === 'object' && val.__type === 'CardboardBox')
      return new CardboardBox(val);

  return val;

  //or if your object is in a context (like window), and there are many of
  //them that could be in there, you can do:
  //
  //if(typeof(val) === 'object' && context[val.__type])
  //    return new context[val.__type](val);
});


console.log(obj);

基本上,将对象类型存储在您知道要在解析 json 时查找的位置。如果您有多个对象可以在单个作用域中实例化,则第二个 parse 方法可能更合适。这也将考虑到 JSON 中不是 CardboardBox 的对象。
编辑:这里是此方法在 jsFiddle 上的操作示例。

是的,那基本上就是我的解决方案。我只是想知道是否有更好的方法。希望JavaScript有适当的类。我猜最终会用一些Java Bean类型的东西。 - Steve
JavaScript并不是“没有适当的类”,而是JSON格式不支持自定义对象。因此,不幸的是,你必须绕过这个问题,据我所知,这(或其变体)是实现它的唯一方法。即使使用Backbone或某些框架进行抽象处理,它也只是在幕后执行类似于这样的操作。 - Chad

1
总的来说,你是正确的:JavaScript 没有内置的方法来序列化除了普通对象以外的任何东西,因此从 JSON 序列化和反序列化不会产生特定的类。所以你需要自己解决序列化/反序列化问题,或者使用提供一些支持的库。
我个人喜欢 Backbone.js 来解决这个问题,因为它处理序列化和反序列化非常好。你可以定义一个模型类,其中包括将其数据保存到服务器的序列化形式的方法,以及将其反序列化回模型的方法。这里的关键设计问题是,在进行反序列化时要知道你正在反序列化的模型:
  • 你可以调用 myModel.fetch() 根据模型 ID 从服务器获取数据,或者
  • 你可以将一堆新数据传递给模型构造函数:new Model(serializedData),或者
  • 你可以将多个模型的数据数组传递给知道模型类型的集合:new ModelCollection(arrayOfSerializedData)
Backbone 不会处理未知类型的数据类型转换。当我遇到这种情况时,通常会采用类似于 @Chad 的回答的方法,但使用一个中介者;你可以将其视为代理模型或工厂:
var classes = {
    CardboardBox: ...,
    AluminumBox: ...
}

function Deserializer(json) {
    // parse if you're actually dealing with a string
    var data = JSON.parse(json),
        // now look for some custom type flag - you'll need to set this yourself
        type = data.type,
        // class lookup, perhaps with a default
        Cls = classes[type] || DefaultType;
    return new Cls(data);
}

var obj = new Deserializer(send);
obj instanceof CardboardBox; // should work

不过,这仍然依赖于自定义标志来切换类型 - 我不确定是否有任何绕过此问题的方法。


所有的答案都很好。谢谢。如果我在反序列化后无法将对象放入正确的类中,代码将变得混乱和/或我最终会在对象上做额外的工作,那么我可能会编写自己的JSON包装器。 - Steve

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接