JamesNK / Newtonsoft.Json

Json.NET is a popular high-performance JSON framework for .NET
https://www.newtonsoft.com/json
MIT License
10.71k stars 3.24k forks source link

j.Value<decimal>("dataKey") error when value is a scientific-notation string (eg. {"dataKey":"5.445E-4"}) #2899

Open dom993 opened 11 months ago

dom993 commented 11 months ago

Source/destination types

JObject j = JObject.Parse(x);
decimal d = j.Value<decimal>("dataKey");

Source/destination JSON

{"dataKey":"5.445E-4"}

Expected behavior

d = 0.0005445

Actual behavior

Exception "input string was not in the correct format"

Steps to reproduce

The client call "decimal d = j.Value<decimal>("dataKey");" ends up in Extensions.cs / internal static U? Convert<T, U>(this T token) where T : JToken? { ... } / line 297 "return (U?)System.Convert.ChangeType(value.Value, targetType, CultureInfo.InvariantCulture);"

This call is fine when the U type is Double as in that case System.Convert.ChangeType(...) calls System.Convert.ToDouble(string, CultureType) which calls Double.Parse(s, NumberStyles.Float | NumberStyles.AllowThousand, ...) 
Note that NumberStyles.Float includes NumberStyles.AllowExponent.

However when the U type is Decimal, System.Convert.ChangeType(...) calls System.Convert.ToDecimal(string, CultureType) which calls Decimal.Parse(s, NumberStyles.Number, ...) which does NOT have the NumberStyle.AllowExponent.

My suggestion is to handle the case of U being Decimal and value.Value being String separately from the generic return:

if ((targetType == typeof(decimal)) && (value.Value != null) && (value.Value.GetType() == typeof(string)))
    return (U?)System.Convert.ChangeType(decimal.Parse(value.Value as string, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture), targetType, CultureInfo.InvariantCulture);
else
    return (U?)System.Convert.ChangeType(value.Value, targetType, CultureInfo.InvariantCulture);