Ruby-on-rails 如何验证字符串是否为 Rails 模型中的 json

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6594257/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 01:32:36  来源:igfitidea点击:

How to validate if a string is json in a Rails model

ruby-on-railsjsonvalidation

提问by Jasper Kennis

I'm building a simple app and want to be able to store json strings in a db. I have a table Interface with a column json, and I want my rails model to validate the value of the string. So something like:

我正在构建一个简单的应用程序,并希望能够在 db 中存储 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

How do I do that?

我怎么做?

回答by polarblau

I suppose you could parse the field in question and see if it throws an error. Here's a simplified example (you might want to drop the double bang for something a bit clearer):

我想你可以解析有问题的字段,看看它是否会引发错误。这是一个简化的示例(您可能想要删除双爆炸以获得更清晰的内容):

require 'json'

class String
  def is_json?
    begin
      !!JSON.parse(self)
    rescue
      false
    end
  end
end

Then you could use this string extension in a custom validator.

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

validate :json_format

protected

  def json_format
    errors[:base] << "not in json format" unless json.is_json?
  end

回答by Alain Beauvois

The best way is to add a method to the JSON module !

最好的方法是在 JSON 模块中添加一个方法!

Put this in your config/application.rb :

把它放在你的 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

Now you'll be enable to use it anywhere ('controller, model, view,...'), just like this :

现在你可以在任何地方使用它('controller, model, view,...'),就像这样:

puts 'it is json' if JSON.is_json?(something)

回答by joost

Currently (Rails 3/Rails 4) I would prefer a custom validator. Also see https://gist.github.com/joost/7ee5fbcc40e377369351.

目前(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

回答by Dmitry Vershinin

I faced another problem using Rails 4.2.4 and PostgreSQL adapter (pg) and custom validator for my json field.

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

In the following example:

在以下示例中:

class SomeController < BaseController
  def update
    @record.json_field = params[:json_field]
  end
end

if you pass invalid JSON to

如果您将无效的 JSON 传递给

params[:json_field]

it is quietly ignored and "nil" is stored in

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

@record.json_field

If you use custom validator like

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

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

you wouldn't see invalid string in

你不会看到无效的字符串

record.json_field

only "nil" value, because rails does type casting before passing your value to validator. In order to overcome this, just use

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

record.json_field_before_type_cast

in your validator.

在您的验证器中。

回答by thisismydesign

If you don't fancy enterprise-style validators or monkey-patching the String class here's a simple solution:

如果您不喜欢企业风格的验证器或猴子修补 String 类,这里有一个简单的解决方案:

class Model < ApplicationRecord
  validate :json_field_format

  def parsed_json_field
    JSON.parse(json_field)
  end

  private

  def json_field_format
    return if json_field.blank?
    begin
      parsed_json_field
    rescue JSON::ParserError => e
      errors[:json_field] << "is not valid JSON" 
    end
  end
end

回答by phlegx

Using JSON parser, pure JSON format validation is possible. ActiveSupport::JSON.decode(value)validates value "123"and 123to true. That is not correct!

使用 JSON 解析器,可以进行纯 JSON 格式验证。ActiveSupport::JSON.decode(value)验证值"123"123为真。那是不正确的!

# 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)
    if value.is_a?(Hash) || value.is_a?(Array)
      value = value.to_json
    elsif value.is_a?(String)
      value = value.strip
    end
    JSON.parse(value)
  rescue JSON::ParserError, TypeError => exception
    record.errors.add(attribute, options[:message], exception_message: exception.message)
  end

end

回答by WystJenkins

The most simple and elegant way, imo. The top upvoted answers will either return true when passing a string containing integers or floats, or throw an error in this case.

最简单优雅的方式,imo。在传递包含整数或浮点数的字符串时,最高投票的答案将返回 true,或者在这种情况下抛出错误。

def valid_json?(string)
    hash = Oj.load(string)
    hash.is_a?(Hash) || hash.is_a?(Array)
rescue Oj::ParseError
    false
end