webentwicklung-frage-antwort-db.com.de

So überprüfen Sie, ob eine Zeichenfolge in einem Rails-Modell json ist

Ich baue eine einfache App und möchte Json-Strings in einer Datenbank speichern können. Ich habe eine Tabellenschnittstelle mit einer Spalte json und möchte, dass mein Rails-Modell den Wert der Zeichenfolge überprüft. So etwas wie:

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

Wie mache ich das?

28
Jasper Kennis

Ich denke, Sie könnten das betreffende Feld analysieren und sehen, ob es einen Fehler ausgibt. Hier ist ein vereinfachtes Beispiel (Sie möchten vielleicht den Doppelschlag für etwas klareres weglassen):

require 'json'

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

Dann können Sie diese Zeichenfolgenerweiterung in einem benutzerdefinierten Prüfer verwenden.

validate :json_format

protected

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

Am besten fügen Sie dem JSON-Modul eine Methode hinzu!

Füge dies in deine config/application.rb ein:

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

Jetzt können Sie es überall verwenden ("Controller, Modell, Ansicht, ..."), genau wie folgt:

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

Derzeit (Rails 3/Rails 4) würde ich einen benutzerdefinierten Validator bevorzugen. Siehe auch 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
15
joost

Ich hatte ein anderes Problem mit dem Rails 4.2.4- und PostgreSQL-Adapter (pg) und dem benutzerdefinierten Validator für mein Json-Feld. 

Im folgenden Beispiel:

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

wenn Sie ungültige JSON an übergeben 

params[:json_field]

es wird leise ignoriert und "null" wird in gespeichert

@record.json_field

Wenn Sie einen benutzerdefinierten Prüfer verwenden, z 

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

sie würden keine ungültige Zeichenfolge in sehen 

record.json_field

nur "Null" -Wert, da Rails das Casting ausführt, bevor der Wert an den Prüfer übergeben wird. Um dies zu überwinden, verwenden Sie einfach

record.json_field_before_type_cast

in Ihrem Validator.

3

Mit dem JSON-Parser ist eine reine JSON-Formatvalidierung möglich. ActiveSupport::JSON.decode(value) validiert den Wert "123" und 123 auf wahr. Das ist nicht richtig!

# 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
0
phlegx

Wenn Sie keine Validatoren im Enterprise-Stil und keine Affen-Patches für die String-Klasse suchen, finden Sie hier eine einfache Lösung:

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
0
thisismydesign

Die einfachste und eleganteste Art, imo. Die am besten bewerteten Antworten geben entweder true zurück, wenn eine Zeichenfolge übergeben wird, die Ganzzahlen oder Gleitkommazahlen enthält, oder werfen in diesem Fall einen Fehler.

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