Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails JSON Serialization of Decimal adds Quotes

I'm using the default JSON serialization for a model that has a number of decimal and integer attributes. An example result is:

{ "user": { "id": 1234, "rating": "98.7" } }

Notice the addition of quotes around the value of "rating". This causes the deserialization library I'm using to incorrectly treat these as strings (instead of decimals). Can Rails be set to not use the quotes for all decimals?

Edit:

I'm on Rails 3.0.7 and Ruby 1.9.2 if that makes a difference.

Edit:

Terminal:

rails g model user rating:decimal
rake db:migrate

Console:

user = User.create(rating: 98.7)
user.to_json
like image 347
Kevin Sylvestre Avatar asked May 25 '11 18:05

Kevin Sylvestre


3 Answers

The only "safe" way to hand decimals from language A to language B is to use a String. If your json contains "rating": 98.79999999999999 it will probably be converted to 98.79999999999998 by your JavaScript runtime.

See BigDecimal as_json documentation:

A BigDecimal would be naturally represented as a JSON number. Most libraries, however, parse non-integer JSON numbers directly as floats. Clients using those libraries would get in general a wrong number and no way to recover other than manually inspecting the string with the JSON code itself.

That’s why a JSON string is returned. The JSON literal is not numeric, but if the other end knows by contract that the data is supposed to be a BigDecimal, it still has the chance to post-process the string and get the real value.

If you want to force Rails not to quote these, you could monkey-patch BigDecimal (see Rails source).

# not needed: to compare with the Numeric implementation
class Numeric
  def as_json(options = nil) self end #:nodoc:
  def encode_json(encoder) to_s end #:nodoc:
end

class BigDecimal
  def as_json(options = nil) self end
  def encode_json(encoder) to_s end #:nodoc:
end
like image 174
Marcel Jackwerth Avatar answered Nov 17 '22 13:11

Marcel Jackwerth


This has changed for Rails 4.0 which has the option ActiveSupport.encode_big_decimal_as_string so that you can specify your BigDecimal serialization preference. See issue 6033

In the meantime, if you're comfortable with the arguments put forward in 6033 and you're running a Rails version lower than 4.0 you can monkey patch BigDecimal as below

require 'bigdecimal'

class BigDecimal
  def as_json(options = nil) #:nodoc:
    if finite?
      self
    else
      NilClass::AS_JSON
    end
  end
end

This solved my issues with RABL pumping out strings for dollar amounts stored as BigDecimal.

like image 43
toxaq Avatar answered Nov 17 '22 11:11

toxaq


if you are using ActiveModel::Serializer you can also use to_f to force the conversion from Decimal to Float type. that wil also trim out the quote for you!

so in your object serializer class. do

def rating
  self.rating.to_f
end
like image 9
alexzg Avatar answered Nov 17 '22 12:11

alexzg