2011-07-06 32 views
25

我正在構建一個簡單的應用程序,並希望能夠將json字符串存儲在數據庫中。我有一個json列的接口,我希望我的rails模型驗證字符串的值。所以像這樣:如何在Rails模型中驗證字符串是否爲json

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 

我該怎麼做?

回答

33

我想你可以解析有問題的字段,看看它是否會拋出錯誤。這裏有一個簡單的例子(你可能要放棄的東西更清楚一點的雙重爆炸):

require 'json' 

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

然後,你可以使用一個自定義的驗證此字符串擴展。

validate :json_format 

protected 

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

而且都變綠了,謝謝!太好的響應時間:)我一直在嘗試做這樣的事情,但我的Rails技能有點塵土飛揚。 –

+0

嗯,這很奇怪。我有rspec測試,其中之一需要一個有效的json字符串作爲json的值,而這些都是綠色的。但是我的黃瓜測試失敗了,並且在rails服務器上測試視圖失敗,說明is_json?是一個未定義的方法。我已經將您在模型下方提出的驗證類放置了,是不是? –

+4

我猜這裏有不同的意見,但似乎大多數人都將他們的核心類擴展放在'config/initializers /'中作爲'* .rb'(自然地),它們在Rails加載後自動加載。另一個選項是'lib /'目錄,但是你必須告訴Rails仍然要加載你的文件。 – polarblau

14

最好的方法是添加一個方法到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) 
14

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

我實際上比接受的答案更喜歡這個想法。只是如何使用MultiJson.load(值)而不是ActiveSupport :: JSON.decode(value)?然後它也會進入救援區... – mfittko

0

使用JSON解析器,純JSON格式驗證是可能的。 ActiveSupport::JSON.decode(value)驗證值"123"123爲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 
3

我面對用Rails 4.2.4和PostgreSQL適配器(PG)和自定義驗證我的JSON領域的又一問題。

在下面的例子:

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

如果傳遞無效的JSON到

params[:json_field] 

它正在悄悄地忽略, 「無」 被保存在

@record.json_field 

如果使用自定義驗證程序,如

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 

「無」值無效的字符串,因爲軌道並輸入您的值傳遞給驗證之前鑄造。爲了解決這個問題,只需在您的驗證器中使用

record.json_field_before_type_cast 

相關問題