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

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

class Interface  true, :length => { :minimum => 3, :maximum => 40 }, :uniqueness => true validates :json, :presence => true, :type => json #SOMETHING LIKE THIS :contains => json #OR THIS end 

我怎么做?

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

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

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

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

最好的方法是向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 

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

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

目前(Rails 3 / Rails 4)我更喜欢自定义validation器 。 另请参阅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 

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

在以下示例中:

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

如果您将无效的JSON传递给

 params[:json_field] 

它被悄悄地忽略了,并且存储了“nil”

 @record.json_field 

如果您使用自定义validation器

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

你不会看到无效的字符串

 record.json_field 

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

 record.json_field_before_type_cast 

在您的validation器中。

使用JSON解析器,可以进行纯JSON格式validation。 ActiveSupport::JSON.decode(value)"123"123validation为true。 那不对!

 # 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 

如果您不喜欢企业级validation器或猴子修补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] << "not is json format" end end end