JamesNK / Newtonsoft.Json

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

Can't de-serialise decimals having no leading zero (regression between NewtonSoft.Json 9.0.1 and 10.0.1) #2946

Closed sylvainrodrigue closed 2 months ago

sylvainrodrigue commented 2 months ago

Source/destination types


    public class TestClass
    {
        public decimal Amount { get; set; }
    }

Source/destination JSON


 "{ \"Amount\": .00}"

"{ \"Amount\": -.23}"

Expected behavior

Actual behavior

Both examples throw an exception with NewtonSoft 10.0.1 or higher. Both works with 9.0.1 or lower.

Exception thrown:

Newtonsoft.Json.JsonReaderException HResult=0x80131500 Message=Input string '.00' is not a valid decimal. Path 'Amount', line 1, position 15. Source=Newtonsoft.Json StackTrace: at Newtonsoft.Json.JsonTextReader.ParseReadNumber(ReadType readType, Char firstChar, Int32 initialPosition) at Newtonsoft.Json.JsonTextReader.ParseNumber(ReadType readType) at Newtonsoft.Json.JsonTextReader.ReadNumberValue(ReadType readType) at Newtonsoft.Json.JsonTextReader.ReadAsDecimal() at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.ReadForType(JsonReader reader, JsonContract contract, Boolean hasConverter) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue) at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent) at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType) at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings) at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value) at NewtonSoft_Test2.Program.Main(String[] args) in C:\Repos_Test\NewtonSoft_Test2\NewtonSoft_Test2\Program.cs:line 17

Steps to reproduce

using Newtonsoft.Json;

namespace NewtonSoft_Test2
{
    internal class Program
    {
        static void Main(string[] args)
        {
           // This works fine with versions 9.0.1 and lower. Doesn't work with 10.0.1 and higher
            var jsonString = "{ \"Amount\": .00}";
            var obj = JsonConvert.DeserializeObject<TestClass>(jsonString);
            Console.WriteLine(obj.Amount);

           // This works fine with versions 9.0.1 and lower. Doesn't work with 10.0.1 and higher
            jsonString = "{ \"Amount\": -.23}";
            obj = JsonConvert.DeserializeObject<TestClass>(jsonString);
            Console.WriteLine(obj.Amount);

            Console.ReadLine();
        }
    }

    public class TestClass
    {
        public decimal Amount { get; set; }
    }
}
elgonzo commented 2 months ago

I am not commenting on whether this is an accidental regression or an intenional fix/change in the behavior of the serializer, but take note that your examples are not valid json. The json format does not allow numbers featuring a decimal dot without a preceding numerical digit. As such, expect that your kind-of-but-not-really json will cause further problems if you ever decide to use something else than Newtonsoft.Json...

sylvainrodrigue commented 2 months ago

You're right: this is not supported by JSON. So this is a fix, not a regression. The problem is our side. Thank you for your quick reply, much appreciated!