Markus Graf
Markus Graf

Reputation: 533

Automatically cast JSON coded data in ruby

My challenge is that in the database, JSON code was untidily stored.

{'isr_comment':'Test Comment',
'isr_depression_1': '1',
'isr_depression_2': '1'
'isr_depression_3': '1'
'isr_tested': 'true'
}

You see, all values are defined as string but some should be integers. It would be the best to have clean data already in the database but I cannot control how the data is entered. However my model looks like this.

class SessionPart < ApplicationRecord
...
  serialize :answers, JSON
...
end

As expected after deserialization is done I get strings as well.

@data=
  {"isr_Comment"=>"Test Comment",
   "isr_depression_1"=>"1",
   "isr_depression_2"=>"1",
   "isr_depression_3"=>"1",
   "isr_tested" => "true"}

But I need to do some calculation with this data so I need all possible values with a meaningful type.

@data=
  {"isr_Comment"=>"Test Comment",
   "isr_depression_1"=>1,
   "isr_depression_2"=>1,
   "isr_depression_3"=>1,
   "isr_tested" => true}

Is there any way to cast such data automatically?

Upvotes: 0

Views: 65

Answers (2)

Qaisar Nadeem
Qaisar Nadeem

Reputation: 2424

You can pass your custom serializer to serialize function. That custom serializer would use JSON as source serializer and update the values as per your requirements.

class SessionPart < ApplicationRecord
...
  serialize :answers, CustomSerializer #CustomSerializer must write 2 class level function named  dump & load for serializing and de-serializing respectively 
...
end


class CustomSerializer
  def self.load(value)
    normalize_hash(JSON.load(value))
  end

  def self.dump(value)
    JSON.dump(value)
  end

  private

  def self.normalize_hash hash
    return hash unless hash.is_a? Hash
    hash.transform_values {|v| normalize(v)}
  end
 
#change this function as per your requirement, Currently it's handling boolean,integer,float & null rule set
def self.normalize(value)
  case (value)
  when 'true'
    true
  when 'false'
    false
  when 'null','nil'
    nil
  when /\A-?\d+\z/
    value.to_i
  when /\A-?\d+\.\d+\z/
    value.to_f
  else
    value.is_a?(Hash) ? normalize_hash(value) : value
  end
end

end

Upvotes: 1

Markus Graf
Markus Graf

Reputation: 533

The suggested CustomSerializer seems to do its job very well, thanks. I did some small adjustments to be able to nest hashes.

class CustomSerializer
  def self.load(value)
    normalize_hash(JSON.load(value))
  end

  def self.dump(value)
    JSON.dump(value)
  end

  private

  def self.normalize_hash hash
    return hash unless hash.is_a? Hash
    hash.transform_values {|v| normalize(v) }
  end

  #change this function as per your requirement, Currently it's handling boolean,integer,float & null rule set
  def self.normalize(value)
    case (value)
    when 'true'
      true
    when 'false'
      false
    when 'null','nil'
      nil
    when /\A-?\d+\z/
      value.to_i
    when /\A-?\d+\.\d+\z/
      value.to_f
    else
      value.is_a?(Hash) ? normalize_hash(value) : value
    end
  end
end

Upvotes: 0

Related Questions