如何在Rails模型中验证字符串是否为json

Jas*_*nis 31 validation json ruby-on-rails

我正在构建一个简单的应用程序,并希望能够在数据库中存储json字符串.我有一个带有json列的表接口,我希望我的rails模型验证字符串的值.所以类似于:

class Interface < ActiveRecord::Base
  attr_accessible :name, :json

  validates :name,  :presence => true,
                    :length   => { :minimum => 3,
                                   :maximum => 40 },
                    :uniqueness => true

  validates :json, :presence => true,
                   :type => json #SOMETHING LIKE THIS
                   :contains => json #OR THIS    
end
Run Code Online (Sandbox Code Playgroud)

我怎么做?

pol*_*lau 40

我想你可以解析有问题的字段,看看它是否会抛出错误.这是一个简化的例子(你可能希望放弃双重爆炸以获得更清晰的东西):

require 'json'

class String
  def is_json?
    begin
      !!JSON.parse(self)
    rescue
      false
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

然后,您可以在自定义验证器中使用此字符串扩展名.

validate :json_format

protected

  def json_format
    errors[:base] << "not in json format" unless json.is_json?
  end
Run Code Online (Sandbox Code Playgroud)

  • 我想有不同的意见,但似乎大多数人将他们的核心类扩展在`config/initializers /`中放置为`*.rb`(自然地),它们在加载Rails后自动加载.另一个选项是`lib /`目录,但是你必须告诉Rails仍然加载你的文件. (4认同)
  • 该解决方案效果很好,我对此表示赞同,但请允许我简短地说:用自己的方法扩展基本对象通常不是最佳实践。我更喜欢实现is_json?辅助类或模块中的方法,并提供“ suspected_json”字符串作为方法的参数。虽然在这里可以使用self非常优雅:) (2认同)

Ala*_*ois 17

最好的方法是向JSON模块添加方法!

把它放在你的config/application.rb中:

module JSON
  def self.is_json?(foo)
    begin
      return false unless foo.is_a?(String)
      JSON.parse(foo).all?
    rescue JSON::ParserError
      false
    end 
  end
end
Run Code Online (Sandbox Code Playgroud)

现在你可以在任何地方使用它('控制器,模型,视图......),就像这样:

puts 'it is json' if JSON.is_json?(something)
Run Code Online (Sandbox Code Playgroud)


joo*_*ost 16

目前(Rails 3/Rails 4)我更喜欢自定义验证器.另请参阅https://gist.github.com/joost/7ee5fbcc40e377369351.

# Put this code in lib/validators/json_validator.rb
# Usage in your model:
#   validates :json_attribute, presence: true, json: true
#
# To have a detailed error use something like:
#   validates :json_attribute, presence: true, json: {message: :some_i18n_key}
# In your yaml use:
#   some_i18n_key: "detailed exception message: %{exception_message}"
class JsonValidator < ActiveModel::EachValidator

  def initialize(options)
    options.reverse_merge!(:message => :invalid)
    super(options)
  end

  def validate_each(record, attribute, value)
    value = value.strip if value.is_a?(String)
    ActiveSupport::JSON.decode(value)
  rescue MultiJson::LoadError, TypeError => exception
    record.errors.add(attribute, options[:message], exception_message: exception.message)
  end

end
Run Code Online (Sandbox Code Playgroud)


Dmi*_*nin 5

我在使用 Rails 4.2.4 和 PostgreSQL 适配器 (pg) 以及 json 字段的自定义验证器时遇到了另一个问题。

在以下示例中:

class SomeController < BaseController
  def update
    @record.json_field = params[:json_field]
  end
end
Run Code Online (Sandbox Code Playgroud)

如果您将无效的 JSON 传递给

params[:json_field]
Run Code Online (Sandbox Code Playgroud)

它被悄悄地忽略,“nil”被存储在

@record.json_field
Run Code Online (Sandbox Code Playgroud)

如果您使用自定义验证器,例如

class JsonValidator < ActiveModel::Validator
  def validate(record)
    begin
      JSON.parse(record.json_field)
    rescue
      errors.add(:json_field, 'invalid json')
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

你不会看到无效的字符串

record.json_field
Run Code Online (Sandbox Code Playgroud)

只有“nil”值,因为 Rails 在将值传递给验证器之前会进行类型转换。为了克服这个问题,只需使用

record.json_field_before_type_cast
Run Code Online (Sandbox Code Playgroud)

在你的验证器中。