4

在我的应用程序中,我在另一个内编写了一个咖啡脚本类(一个实例class A包括一组对象,这些对象的实例class B作为它们的属性之一)。然后我发现了cmather 关于 EJSON 的视频,并认为将它与我的对象一起使用会很酷。但是,Meteor 似乎无法正确处理其他 EJSON 对象中的 EJSON 对象 -class A可以将其保存到数据存储中,并且在查询时以 a 的形式返回class A,但class B最终从集合中以 aObject而不是 a 的形式返回class B实例。当我运行一些测试代码时,嵌入的 EJSON 对象起初似乎可以工作(在 initial 之后collection.insert()),但它返回了普通的Objects 甚至刷新浏览器后结构不正确的对象。我的理论是 minimongo 和服务器端 mongo 的行为存在一些不匹配,但可能还有其他原因。

那么,有没有办法将一个 EJSON 对象嵌入到另一个对象中?也许我的代码有缺陷?这只是一个坏主意吗?我本可以在它的 中class A实例化class B项目本身constructor,但似乎 EJSON 应该能够处理这个问题。如果您认为这是一个错误,我很乐意在 github 上提交一个问题,但我想我会先在这里检查。

如果你需要代码来试试这个,你可以试试下面的代码,它设置了两个基本相同的类,一个是 called Inner,一个是 called Outer,并创建了一个名为 的实例,Outer其中outer包含一个 的实例Inner作为属性innerHere。在控制台中,键入testCollection.insert({outerHere: outer}。现在,testCollection.findOne()可能会给您一个对象,其中该对象的innerHere属性是 的正确实例Inner,但是如果您刷新浏览器,相同的命令可能会返回不同的内容。

如果这个问题很难理解,请告诉我,我会尽力澄清。

设置它的代码(只需.coffee在新项目的根目录中创建一个文件):

@testCollection = new Meteor.Collection("test")

class @Outer
  constructor: (value) ->
    @value = value
  clone: ->
    new Outer(@value)
  equals: (other) ->
    _.isEqual(@, other)
  typeName: ->
    "Outer"
  toJSONValue: ->
    value: @value


EJSON.addType("Outer", (value)->
  new Outer(value)
)

class @Inner
  constructor: (value) ->
    @value = value
  clone: ->
    new Inner(@value)
  equals: (other) ->
    _.isEqual(@, other)
  typeName: ->
    "Inner"
  toJSONValue: ->
    value: @value


EJSON.addType("Inner", (value)->
  new Inner(value)
)

@outer = new Outer({innerHere: new Inner ("inner value")})
4

1 回答 1

4

当 EJSON 调用 Outer 类型的 toJSONValue 时,它​​不会递归到结果中以自动检测内部类型。同样,在 fromJSONValue(您传递给 EJSON.addType 方法的函数)中,您将获得一个 JSON 值对象(toJSONValue 返回的任何结果),并由您来处理它。为了更好地理解转换过程,让我们来看一个给定类的示例。

假设我们要通过网络传递 Outer 类的实例(就像作为方法调用中的参数一样)。

myOuter = new Outer({innerHere: new Inner('inner value')});

Meteor 将遵循类似于以下的步骤:

EJSON.stringify(myOuter) => 
  var jsonValue = EJSON.toJSONValue(myOuter);
  var json = JSON.stringify(jsonValue);

对 EJSON.toJSONValue 的调用会创建一个具有 $type 和 $value 属性的新对象。$value 属性的值是对对象调用 toJSONValue 的结果。所以 jsonValue 是一个看起来像这样的对象:

{
  $type: 'Outer',
  $value: {
    innerHere: {
      value: 'inner value'
    }
  }
}

调用 JSON.stringify(jsonValue) 会生成如下所示的 JSON 字符串:

"{"$type":"Outer","$value":{"value":{"innerHere":{"value":"inner value"}}}}"

如果您希望 innerHere 属性是 EJSON 类型,我们还需要在该对象上调用 EJSON.toJSONValue(从 Outer 的 toJSONValue 方法)。例如(在 js 中):

Outer.prototype.toJSONValue = function () {
  return {
    value: EJSON.toJSONValue(this.value)
  };
};

现在假设我们像这样创建一个新的 Outer 实例:

myOuter = new Outer(new Inner('inner value'));

然后我们调用 EJSON.toJSONValue(myOuter):

{
  $type: 'Outer',
  $value: {
    value: {
      $type: 'Inner',
      $value: {
        value: 'inner value'
      }
    }
  }
}

通过网络发送的生成的 json 字符串如下所示:

"{"$type":"Outer","$value":{"value":{"$type":"Inner","$value":{"value":"inner value"}}}}"

好的,现在我们的 fromJSONValue 函数会发生什么?我们将得到一个看起来与返回的 toJSONValue 相似的对象。所以我们需要在每个我们知道是自定义类型的属性上调用 EJSON.fromJSONValue,然后再将它们传递给 Outer 的构造函数。在本例中,您可以这样做:

EJSON.addType('Outer', function (jsonValue) {
  var inner = EJSON.fromJSONValue(jsonValue.value);
  return new Outer(inner);
});

可用于测试序列化和反序列化的两种方法是:

var serialized = EJSON.stringify(myOuter);
var deserialized = EJSON.parse(serialized); // is the resulting object what you expect?

希望这可以帮助!

于 2013-08-04T05:47:13.247 回答