2

我正在使用 data_mapper/sinatra 并尝试使用 attr_accessor 创建一些属性。以下示例代码:

require 'json'
class Person
  include DataMapper::Resource

  property :id,          Serial
  property :first_name,  String
  attr_accessor  :last_name
end

ps = Person.new
ps.first_name = "Mike"
ps.last_name = "Smith"
p ps.to_json

产生这个输出:

"{\"id\":null,\"first_name\":\"Mike\"}"

显然,我希望它同时给我名字和姓氏属性。关于如何让它以人们期望的方式工作的任何想法,以便我的 json 具有所有属性?

另外,请随时解释为什么我的期望(我会得到所有属性)是不正确的。我猜一些内部属性列表没有添加 attr_accessor 实例变量或其他东西。但即便如此,为什么呢?

4

2 回答 2

2

Datamapper 有它自己的序列化库,它为任何 Datamapper 资源dm-serializer提供方法。to_json如果您require 'data_mapper'在代码中需要 Datamapper,则您正在使用需要 dm-serializer 作为其一部分的meta data_mapper-gem设置

提供的to_json方法dm-serializer仅序列化对象的 Datamapper 属性(即您使用 指定的那些property),而不是“普通”属性(您使用 定义的attr_accessor)。这就是为什么你得到idfirst_name不是last_name

为了避免使用dm-serializer,您需要明确要求您需要的那些库,而不是依赖data_mapper. 你至少需要dm-core,也许还有其他人。

“普通”库在对象的默认调用json中不包含任何属性,它只是使用 objects方法。所以在这种情况下,如果你用 替换,你会得到类似的东西。to_jsonto_srequire 'data_mapper'require 'dm-core'"\"#<Person:0x000001013a0320>\""

要创建您自己的对象的 json 表示,您需要创建自己的to_json方法。一个简单的例子是在 json 中硬编码你想要的属性:

def to_json
  {:id => id, :first_name => first_name, :last_name => last_name}.to_json
end

您可以创建一个查看对象属性和属性的方法,并从中创建适当的 json,而不是通过这种方式对其进行硬编码。

请注意,如果您创建自己的to_json方法,您仍然可以调用require 'data_mapper',您to_json将替换dm-serializer. 实际上dm-serializer还添加了一个as_json方法,您可以使用它来创建组合to_json方法,例如:

def to_json
  as_json.merge({:last_name => last_name}).to_json
end
于 2012-08-19T07:34:08.530 回答
1

感谢马特,我做了一些挖掘并找到了 dm-serializer 的 to_json 方法的 :method 参数。他们的 to_json 方法相当不错,基本上只是 as_json 辅助方法的包装器,所以我通过添加几行来覆盖它:

  if options[:include_attributes]
    options[:methods] = [] if options[:methods].nil?
    options[:methods].concat(model.attributes).uniq!
  end

完成的方法覆盖如下所示:

module DataMapper
  module Serializer

    def to_json(*args)
      options = args.first
      options = {} unless options.kind_of?(Hash)

      if options[:include_attributes]
        options[:methods] = [] if options[:methods].nil?
        options[:methods].concat(model.attributes).uniq!
      end

      result = as_json(options)

      # default to making JSON
      if options.fetch(:to_json, true)
        MultiJson.dump(result)
      else
        result
      end
    end

  end
end

这与我添加到与我的模型一起使用的基本模块中的属性方法一起使用。相关部分如下:

module Base

  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods

    def attr_accessor(*vars)
      @attributes ||= []
      @attributes.concat vars
      super(*vars)
    end

    def attributes
      @attributes || []
    end
  end

  def attributes
    self.class.attributes
  end

end

现在我原来的例子:

require 'json'
class Person
  include DataMapper::Resource
  include Base

  property :id,          Serial
  property :first_name,  String
  attr_accessor  :last_name
end

ps = Person.new
ps.first_name = "Mike"
ps.last_name = "Smith"
p ps.to_json :include_attributes => true

使用新的选项参数按预期工作。

为了有选择地获得我想要的属性而不必做额外的工作,我可以做的就是将属性名称传递给 :methods 参数。

p ps.to_json :methods => [:last_name]

或者,因为我已经Base上课了:

p ps.to_json :methods => Person.attributes

现在我只需要弄清楚我想如何支持集合。

于 2012-08-20T05:08:36.153 回答