Rails decimal returned as string in API
I have a field called price in my db which is a decimal field and in my JSON API response it is returned as a String.
I wanted to apply some validations to the field that would allow max 2 digits after period and found out that if I use decimal field I can then apply the precision on DB level.
t.decimal "price", precision: 10, scale: 2
Then I want to calculate total price in a service object:
services/statistics/monthly_rides_generator.rb
class CurrentMonthRidesGenerator
def initialize(current_user)
@current_user = current_user
end
def call
user_current_month_rides_by_day.map do |date, rides_on_day|
{
day: formatted_date(date),
total_distance: total_distance(rides_on_day),
avg_ride: avg_ride(rides_on_day),
avg_price: avg_price(rides_on_day),
total_price: total_price(rides_on_day)
}
end
end
...
def total_price(rides)
rides.map(&:price).sum
end
end
app/api/statistics/stats_api.rb
get '/current_month' do
Statistics::CurrentMonthRidesGenerator.new(current_user).call
end
but in API response this field is a String.
{
"day": "November, 8th",
"total_distance": "9km",
"avg_ride": "9km",
"avg_price": "100.0PLN",
"total_price": "100.0"
}
I want this field to be returned as it was saved because I need a float/decimal number in the front end to then do other calculations.
Why is it returning a String when it is a decimal field? How can I fix it?
ruby-on-rails
add a comment |
I have a field called price in my db which is a decimal field and in my JSON API response it is returned as a String.
I wanted to apply some validations to the field that would allow max 2 digits after period and found out that if I use decimal field I can then apply the precision on DB level.
t.decimal "price", precision: 10, scale: 2
Then I want to calculate total price in a service object:
services/statistics/monthly_rides_generator.rb
class CurrentMonthRidesGenerator
def initialize(current_user)
@current_user = current_user
end
def call
user_current_month_rides_by_day.map do |date, rides_on_day|
{
day: formatted_date(date),
total_distance: total_distance(rides_on_day),
avg_ride: avg_ride(rides_on_day),
avg_price: avg_price(rides_on_day),
total_price: total_price(rides_on_day)
}
end
end
...
def total_price(rides)
rides.map(&:price).sum
end
end
app/api/statistics/stats_api.rb
get '/current_month' do
Statistics::CurrentMonthRidesGenerator.new(current_user).call
end
but in API response this field is a String.
{
"day": "November, 8th",
"total_distance": "9km",
"avg_ride": "9km",
"avg_price": "100.0PLN",
"total_price": "100.0"
}
I want this field to be returned as it was saved because I need a float/decimal number in the front end to then do other calculations.
Why is it returning a String when it is a decimal field? How can I fix it?
ruby-on-rails
Should I useMoneygem to convert it to Float?
– jedi
Nov 18 '18 at 1:22
add a comment |
I have a field called price in my db which is a decimal field and in my JSON API response it is returned as a String.
I wanted to apply some validations to the field that would allow max 2 digits after period and found out that if I use decimal field I can then apply the precision on DB level.
t.decimal "price", precision: 10, scale: 2
Then I want to calculate total price in a service object:
services/statistics/monthly_rides_generator.rb
class CurrentMonthRidesGenerator
def initialize(current_user)
@current_user = current_user
end
def call
user_current_month_rides_by_day.map do |date, rides_on_day|
{
day: formatted_date(date),
total_distance: total_distance(rides_on_day),
avg_ride: avg_ride(rides_on_day),
avg_price: avg_price(rides_on_day),
total_price: total_price(rides_on_day)
}
end
end
...
def total_price(rides)
rides.map(&:price).sum
end
end
app/api/statistics/stats_api.rb
get '/current_month' do
Statistics::CurrentMonthRidesGenerator.new(current_user).call
end
but in API response this field is a String.
{
"day": "November, 8th",
"total_distance": "9km",
"avg_ride": "9km",
"avg_price": "100.0PLN",
"total_price": "100.0"
}
I want this field to be returned as it was saved because I need a float/decimal number in the front end to then do other calculations.
Why is it returning a String when it is a decimal field? How can I fix it?
ruby-on-rails
I have a field called price in my db which is a decimal field and in my JSON API response it is returned as a String.
I wanted to apply some validations to the field that would allow max 2 digits after period and found out that if I use decimal field I can then apply the precision on DB level.
t.decimal "price", precision: 10, scale: 2
Then I want to calculate total price in a service object:
services/statistics/monthly_rides_generator.rb
class CurrentMonthRidesGenerator
def initialize(current_user)
@current_user = current_user
end
def call
user_current_month_rides_by_day.map do |date, rides_on_day|
{
day: formatted_date(date),
total_distance: total_distance(rides_on_day),
avg_ride: avg_ride(rides_on_day),
avg_price: avg_price(rides_on_day),
total_price: total_price(rides_on_day)
}
end
end
...
def total_price(rides)
rides.map(&:price).sum
end
end
app/api/statistics/stats_api.rb
get '/current_month' do
Statistics::CurrentMonthRidesGenerator.new(current_user).call
end
but in API response this field is a String.
{
"day": "November, 8th",
"total_distance": "9km",
"avg_ride": "9km",
"avg_price": "100.0PLN",
"total_price": "100.0"
}
I want this field to be returned as it was saved because I need a float/decimal number in the front end to then do other calculations.
Why is it returning a String when it is a decimal field? How can I fix it?
ruby-on-rails
ruby-on-rails
edited Nov 18 '18 at 2:00
asked Nov 18 '18 at 1:08
jedi
487416
487416
Should I useMoneygem to convert it to Float?
– jedi
Nov 18 '18 at 1:22
add a comment |
Should I useMoneygem to convert it to Float?
– jedi
Nov 18 '18 at 1:22
Should I use
Money gem to convert it to Float?– jedi
Nov 18 '18 at 1:22
Should I use
Money gem to convert it to Float?– jedi
Nov 18 '18 at 1:22
add a comment |
3 Answers
3
active
oldest
votes
It's returning as a string because Decimal/BigDecimal in ruby is a very precise number. Javascript/JSON doesn't have a data type that is as precise as ruby's BigDecimal.
You can easily see this in your browser if you open up the console:

If you convert the value to a float in your serializer then it will be a float in the JSON object that is returned by your API, but you'll want to be careful of how your clients use this data. If they don't use a library that can handle precision then you're going to get rounding errors and often be off on your calculations by a penny.
I've been using the decimal.js library for things like this and it works out great. https://github.com/MikeMcl/decimal.js/
Thanx for the rationale. I have some precision validations in db that rounds the number to 2 places after period and my clients will only get numbers with max 2 digits after period so that's not a problem
– jedi
Nov 18 '18 at 1:55
add a comment |
One way to solve this is to cast it to the desired type in a serializer:
# app/serializers/your_model_serializer.rb
class YourModelSerializer < ActiveModel::Serializer
attributes :day,
:total_distance,
:avg_ride,
:avg_price,
:total_price
def total_price
object.total_price.to_f
end
end
What if this method is not defined on a Model? I have this method defined in a service object. Will that still work?
– jedi
Nov 18 '18 at 1:34
Sure, you could call the service object from the serializer instead ofobject.total_price.to_f. EDIT: That is assuming you are calling an action on the controller associated with the serializer (e.g.YourModelControllerandYourModelSerializer).
– Troy Carlson
Nov 18 '18 at 1:45
1
If you update the question with more details about where thetotal_pricemethod is defined, what the controller looks like, etc. I can provide a more definitive answer.
– Troy Carlson
Nov 18 '18 at 1:49
Can I not simply call to_f on it?
– jedi
Nov 18 '18 at 2:24
Well I'm not sure how Rails behaves when you call a service object directly within a route like you are doing...my solution works when you are using a more traditional controller + serializer convention, where subclasses ofActiveModel::Serializergive you more control over how Rails serializes the response.
– Troy Carlson
Nov 19 '18 at 3:11
|
show 1 more comment
I decided to solve this problem by changing the column type to Float, renaming it to price_cents, adding money-rails and monetizing the column. Then I just call to_f or round the value right before displaying it or sending it to front-end in API response.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53357032%2frails-decimal-returned-as-string-in-api%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
It's returning as a string because Decimal/BigDecimal in ruby is a very precise number. Javascript/JSON doesn't have a data type that is as precise as ruby's BigDecimal.
You can easily see this in your browser if you open up the console:

If you convert the value to a float in your serializer then it will be a float in the JSON object that is returned by your API, but you'll want to be careful of how your clients use this data. If they don't use a library that can handle precision then you're going to get rounding errors and often be off on your calculations by a penny.
I've been using the decimal.js library for things like this and it works out great. https://github.com/MikeMcl/decimal.js/
Thanx for the rationale. I have some precision validations in db that rounds the number to 2 places after period and my clients will only get numbers with max 2 digits after period so that's not a problem
– jedi
Nov 18 '18 at 1:55
add a comment |
It's returning as a string because Decimal/BigDecimal in ruby is a very precise number. Javascript/JSON doesn't have a data type that is as precise as ruby's BigDecimal.
You can easily see this in your browser if you open up the console:

If you convert the value to a float in your serializer then it will be a float in the JSON object that is returned by your API, but you'll want to be careful of how your clients use this data. If they don't use a library that can handle precision then you're going to get rounding errors and often be off on your calculations by a penny.
I've been using the decimal.js library for things like this and it works out great. https://github.com/MikeMcl/decimal.js/
Thanx for the rationale. I have some precision validations in db that rounds the number to 2 places after period and my clients will only get numbers with max 2 digits after period so that's not a problem
– jedi
Nov 18 '18 at 1:55
add a comment |
It's returning as a string because Decimal/BigDecimal in ruby is a very precise number. Javascript/JSON doesn't have a data type that is as precise as ruby's BigDecimal.
You can easily see this in your browser if you open up the console:

If you convert the value to a float in your serializer then it will be a float in the JSON object that is returned by your API, but you'll want to be careful of how your clients use this data. If they don't use a library that can handle precision then you're going to get rounding errors and often be off on your calculations by a penny.
I've been using the decimal.js library for things like this and it works out great. https://github.com/MikeMcl/decimal.js/
It's returning as a string because Decimal/BigDecimal in ruby is a very precise number. Javascript/JSON doesn't have a data type that is as precise as ruby's BigDecimal.
You can easily see this in your browser if you open up the console:

If you convert the value to a float in your serializer then it will be a float in the JSON object that is returned by your API, but you'll want to be careful of how your clients use this data. If they don't use a library that can handle precision then you're going to get rounding errors and often be off on your calculations by a penny.
I've been using the decimal.js library for things like this and it works out great. https://github.com/MikeMcl/decimal.js/
answered Nov 18 '18 at 1:51
Jimmy Baker
2,2871520
2,2871520
Thanx for the rationale. I have some precision validations in db that rounds the number to 2 places after period and my clients will only get numbers with max 2 digits after period so that's not a problem
– jedi
Nov 18 '18 at 1:55
add a comment |
Thanx for the rationale. I have some precision validations in db that rounds the number to 2 places after period and my clients will only get numbers with max 2 digits after period so that's not a problem
– jedi
Nov 18 '18 at 1:55
Thanx for the rationale. I have some precision validations in db that rounds the number to 2 places after period and my clients will only get numbers with max 2 digits after period so that's not a problem
– jedi
Nov 18 '18 at 1:55
Thanx for the rationale. I have some precision validations in db that rounds the number to 2 places after period and my clients will only get numbers with max 2 digits after period so that's not a problem
– jedi
Nov 18 '18 at 1:55
add a comment |
One way to solve this is to cast it to the desired type in a serializer:
# app/serializers/your_model_serializer.rb
class YourModelSerializer < ActiveModel::Serializer
attributes :day,
:total_distance,
:avg_ride,
:avg_price,
:total_price
def total_price
object.total_price.to_f
end
end
What if this method is not defined on a Model? I have this method defined in a service object. Will that still work?
– jedi
Nov 18 '18 at 1:34
Sure, you could call the service object from the serializer instead ofobject.total_price.to_f. EDIT: That is assuming you are calling an action on the controller associated with the serializer (e.g.YourModelControllerandYourModelSerializer).
– Troy Carlson
Nov 18 '18 at 1:45
1
If you update the question with more details about where thetotal_pricemethod is defined, what the controller looks like, etc. I can provide a more definitive answer.
– Troy Carlson
Nov 18 '18 at 1:49
Can I not simply call to_f on it?
– jedi
Nov 18 '18 at 2:24
Well I'm not sure how Rails behaves when you call a service object directly within a route like you are doing...my solution works when you are using a more traditional controller + serializer convention, where subclasses ofActiveModel::Serializergive you more control over how Rails serializes the response.
– Troy Carlson
Nov 19 '18 at 3:11
|
show 1 more comment
One way to solve this is to cast it to the desired type in a serializer:
# app/serializers/your_model_serializer.rb
class YourModelSerializer < ActiveModel::Serializer
attributes :day,
:total_distance,
:avg_ride,
:avg_price,
:total_price
def total_price
object.total_price.to_f
end
end
What if this method is not defined on a Model? I have this method defined in a service object. Will that still work?
– jedi
Nov 18 '18 at 1:34
Sure, you could call the service object from the serializer instead ofobject.total_price.to_f. EDIT: That is assuming you are calling an action on the controller associated with the serializer (e.g.YourModelControllerandYourModelSerializer).
– Troy Carlson
Nov 18 '18 at 1:45
1
If you update the question with more details about where thetotal_pricemethod is defined, what the controller looks like, etc. I can provide a more definitive answer.
– Troy Carlson
Nov 18 '18 at 1:49
Can I not simply call to_f on it?
– jedi
Nov 18 '18 at 2:24
Well I'm not sure how Rails behaves when you call a service object directly within a route like you are doing...my solution works when you are using a more traditional controller + serializer convention, where subclasses ofActiveModel::Serializergive you more control over how Rails serializes the response.
– Troy Carlson
Nov 19 '18 at 3:11
|
show 1 more comment
One way to solve this is to cast it to the desired type in a serializer:
# app/serializers/your_model_serializer.rb
class YourModelSerializer < ActiveModel::Serializer
attributes :day,
:total_distance,
:avg_ride,
:avg_price,
:total_price
def total_price
object.total_price.to_f
end
end
One way to solve this is to cast it to the desired type in a serializer:
# app/serializers/your_model_serializer.rb
class YourModelSerializer < ActiveModel::Serializer
attributes :day,
:total_distance,
:avg_ride,
:avg_price,
:total_price
def total_price
object.total_price.to_f
end
end
answered Nov 18 '18 at 1:24
Troy Carlson
2,0071022
2,0071022
What if this method is not defined on a Model? I have this method defined in a service object. Will that still work?
– jedi
Nov 18 '18 at 1:34
Sure, you could call the service object from the serializer instead ofobject.total_price.to_f. EDIT: That is assuming you are calling an action on the controller associated with the serializer (e.g.YourModelControllerandYourModelSerializer).
– Troy Carlson
Nov 18 '18 at 1:45
1
If you update the question with more details about where thetotal_pricemethod is defined, what the controller looks like, etc. I can provide a more definitive answer.
– Troy Carlson
Nov 18 '18 at 1:49
Can I not simply call to_f on it?
– jedi
Nov 18 '18 at 2:24
Well I'm not sure how Rails behaves when you call a service object directly within a route like you are doing...my solution works when you are using a more traditional controller + serializer convention, where subclasses ofActiveModel::Serializergive you more control over how Rails serializes the response.
– Troy Carlson
Nov 19 '18 at 3:11
|
show 1 more comment
What if this method is not defined on a Model? I have this method defined in a service object. Will that still work?
– jedi
Nov 18 '18 at 1:34
Sure, you could call the service object from the serializer instead ofobject.total_price.to_f. EDIT: That is assuming you are calling an action on the controller associated with the serializer (e.g.YourModelControllerandYourModelSerializer).
– Troy Carlson
Nov 18 '18 at 1:45
1
If you update the question with more details about where thetotal_pricemethod is defined, what the controller looks like, etc. I can provide a more definitive answer.
– Troy Carlson
Nov 18 '18 at 1:49
Can I not simply call to_f on it?
– jedi
Nov 18 '18 at 2:24
Well I'm not sure how Rails behaves when you call a service object directly within a route like you are doing...my solution works when you are using a more traditional controller + serializer convention, where subclasses ofActiveModel::Serializergive you more control over how Rails serializes the response.
– Troy Carlson
Nov 19 '18 at 3:11
What if this method is not defined on a Model? I have this method defined in a service object. Will that still work?
– jedi
Nov 18 '18 at 1:34
What if this method is not defined on a Model? I have this method defined in a service object. Will that still work?
– jedi
Nov 18 '18 at 1:34
Sure, you could call the service object from the serializer instead of
object.total_price.to_f. EDIT: That is assuming you are calling an action on the controller associated with the serializer (e.g. YourModelController and YourModelSerializer).– Troy Carlson
Nov 18 '18 at 1:45
Sure, you could call the service object from the serializer instead of
object.total_price.to_f. EDIT: That is assuming you are calling an action on the controller associated with the serializer (e.g. YourModelController and YourModelSerializer).– Troy Carlson
Nov 18 '18 at 1:45
1
1
If you update the question with more details about where the
total_price method is defined, what the controller looks like, etc. I can provide a more definitive answer.– Troy Carlson
Nov 18 '18 at 1:49
If you update the question with more details about where the
total_price method is defined, what the controller looks like, etc. I can provide a more definitive answer.– Troy Carlson
Nov 18 '18 at 1:49
Can I not simply call to_f on it?
– jedi
Nov 18 '18 at 2:24
Can I not simply call to_f on it?
– jedi
Nov 18 '18 at 2:24
Well I'm not sure how Rails behaves when you call a service object directly within a route like you are doing...my solution works when you are using a more traditional controller + serializer convention, where subclasses of
ActiveModel::Serializer give you more control over how Rails serializes the response.– Troy Carlson
Nov 19 '18 at 3:11
Well I'm not sure how Rails behaves when you call a service object directly within a route like you are doing...my solution works when you are using a more traditional controller + serializer convention, where subclasses of
ActiveModel::Serializer give you more control over how Rails serializes the response.– Troy Carlson
Nov 19 '18 at 3:11
|
show 1 more comment
I decided to solve this problem by changing the column type to Float, renaming it to price_cents, adding money-rails and monetizing the column. Then I just call to_f or round the value right before displaying it or sending it to front-end in API response.
add a comment |
I decided to solve this problem by changing the column type to Float, renaming it to price_cents, adding money-rails and monetizing the column. Then I just call to_f or round the value right before displaying it or sending it to front-end in API response.
add a comment |
I decided to solve this problem by changing the column type to Float, renaming it to price_cents, adding money-rails and monetizing the column. Then I just call to_f or round the value right before displaying it or sending it to front-end in API response.
I decided to solve this problem by changing the column type to Float, renaming it to price_cents, adding money-rails and monetizing the column. Then I just call to_f or round the value right before displaying it or sending it to front-end in API response.
answered Nov 18 '18 at 22:04
jedi
487416
487416
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53357032%2frails-decimal-returned-as-string-in-api%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Should I use
Moneygem to convert it to Float?– jedi
Nov 18 '18 at 1:22