Rails JSON Serialization of Decimal adds Quotes

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

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.


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