47

For some reason, when I send this JSON to an action:

{"BaseLoanAmount": 5000}

which is supposed to be bound to a model with a decimal property named "BaseLoanAmount", it doesn't bind, it just stays 0. But if I send:

{"BaseLoanAmount": 5000.00}

it does bind the property, but why? Can't 5000 be converted to a decimal event if it doesn't have decimal numbers?

ryudice
  • 36,476
  • 32
  • 115
  • 163

2 Answers2

75

After stepping into asp.net mvc's source code, it seemsd the problem is that for the conversion asp.net mvc uses the framework's type converter, which for some reason returns false for an int to decimal conversion, I ended up using a custom model binder provider and model binder for decimals, you can see it here:

public class DecimalModelBinder : DefaultModelBinder
{
    #region Implementation of IModelBinder

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (valueProviderResult.AttemptedValue.Equals("N.aN") ||
            valueProviderResult.AttemptedValue.Equals("NaN") ||
            valueProviderResult.AttemptedValue.Equals("Infini.ty") ||
            valueProviderResult.AttemptedValue.Equals("Infinity") ||
            string.IsNullOrEmpty(valueProviderResult.AttemptedValue))
            return 0m;

       return Convert.ToDecimal(valueProviderResult.AttemptedValue);
    }    

    #endregion
}

To register this ModelBinder, just put the following line inside Application_Start():

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
Saeed
  • 3,415
  • 3
  • 24
  • 41
ryudice
  • 36,476
  • 32
  • 115
  • 163
  • 4
    Boolean expression in `return` statement `valueProviderResult==null` cannot evaluate to true, since `valueProviderResult.AttemptedValue` before that would throw a null-ref exception. – Diego Mar 28 '12 at 20:53
  • 2
    note: Phil Haack also recommends this, http://haacked.com/archive/2011/03/19/fixing-binding-to-decimals.aspx – Nathan Koop Aug 03 '12 at 15:24
  • 2
    I've been using your original solution for exactly this problem - thanks! One issue: nulls destined for decimal? fields are assigned zero instead. I finally went with checking `valueProviderResult.RawValue == null` to determine whether to assign the type's default value. – Matt Oct 22 '12 at 22:04
  • 1
    Approach worked fine for me, however the code seems to be a bit inconsistent. `valueProviderResult` should be checked for `null` first to avoid `NullReferenceException`, currently base binding will never occur. Then, `string.IsNullOrEmpty(valueProviderResult.AttemptedValue)` should be the first condition to check as it again will cause `NullReferenceException` in current implementation. Also, it would be nice to check for `-Infinity` value. – Eadel Dec 23 '14 at 13:16
13

Try sending it like this:

{ "BaseLoanAmount": "5000" }
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • 2
    I use JSON.stringify to prepare the JSON, is there a server side solution for this? I dont want to have to mess with the client code since it's ASP.NET MVC who is not doing its job right. – ryudice Apr 18 '11 at 05:55
  • 2
    @ryudice, I am afraid that's how the javascript serializer deals with numeric formats. Things might get even messier when you start using nullable decimals, floats, ... You will find many discrepancies. Anyways according to the JSON specification quotes should always be used so I am not quite sure who is not doing his job correctly. So you might need to convert the numeric value to a string before stringifying it on the client side. – Darin Dimitrov Apr 18 '11 at 05:57
  • I just found out that if I change the property type to double it binds ok, do you know if the problem could be in the value provider or in the model binder? and do you know if there is a value provider that uses JSON.net instead of the frameworks javascript deserializer? – ryudice Apr 18 '11 at 06:03
  • @ryudice, *the problem* is in the value provider. I don't know a provider that uses JSON.NET but it seems like an easy job to write one. You just need to ensure to remove the older one or things might get even messier :-) – Darin Dimitrov Apr 18 '11 at 06:16
  • Yes, if the decimal is in string format then it's correctly parsed on the server, at least in my testing. – Stephen Patten May 30 '12 at 21:38
  • i use JSON.stringify but it does not work check it out - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify – ni3.net Oct 31 '14 at 06:51