【问题标题】:Can I embed an EJSON object inside another EJSON object?我可以在另一个 EJSON 对象中嵌入一个 EJSON 对象吗?
【发布时间】:2013-08-04 13:47:13
【问题描述】:

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

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

如果你需要代码来试试这个,你可以试试下面的代码,它设置了两个基本相同的类,一个叫做Inner,一个叫做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")})

【问题讨论】:

标签: meteor


【解决方案1】:

当 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?

希望这会有所帮助!

【讨论】:

  • 非常有帮助的答案!最后的测试方法是一个非常好的主意 - 看起来非常适合 TDD EJSON 代码。
猜你喜欢
  • 2013-03-12
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
  • 2016-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多