Rails JSON Serialization of Decimal adds Quotes

Ruby on-Rails

Ruby on-Rails Problem Overview


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

Ruby on-Rails Solutions


Solution 1 - Ruby on-Rails

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

Solution 2 - Ruby on-Rails

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.

Solution 3 - Ruby on-Rails

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

Solution 4 - Ruby on-Rails

With Rails 5 encode_big_decimal_as_string doesn't work (it was deprecated so no surprise there).

If you add jbuilder to your application

# Gemfile
gem 'jbuilder', '~> 2.5'

Then just create a json view that casts the decimal to a float just for the view, you should be golden.

# app/views/yourmodel/index.json.jbuilder
json.array! @yourmodels do |yourmodel|
  json.attributethatisadecimal yourmodel.attributethatisadecimal.to_f
end

This worked well for me - a bit more work (because you have to map your model to jbuilder) but this approach seems pretty clean.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKevin SylvestreView Question on Stackoverflow
Solution 1 - Ruby on-RailsMarcel JackwerthView Answer on Stackoverflow
Solution 2 - Ruby on-RailstoxaqView Answer on Stackoverflow
Solution 3 - Ruby on-RailsalexzgView Answer on Stackoverflow
Solution 4 - Ruby on-RailsjfgrissomView Answer on Stackoverflow