Reputation: 63
I have updated my Newtonsoft.Json version from "8.0.3" to "9.0.1", After the change, I started to face some converting problems between doubles and strings.
Here's some code:
public class KeyValue
{
public string Key { get; set; }
public string Value { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
var json = "{\"Key\": 'test', \"Value\": 210001.0}";
var kv = JsonConvert.DeserializeObject<KeyValue>(json);
}
In Newtonsoft.Json version "8.0.3" - the output of the class would be: Key - "test" Value - "210001" // No .0
In Newtonsoft.Json version "9.0.1" the output of the class would be Key - "test" Value - "210001.0"
This happens only when the value is 210001.0 - for 210001.1 it would not happen. Now I understand that the new version solves this problem better, but I have lot's of external code that depends on the old version solution. How can I achieve the old versions solution?
Upvotes: 6
Views: 8244
Reputation: 1130
This happens, because apparently authors of Newtonsoft.Json
changed the default value of JsonSerializerSettings.FloatParseHandling
. This changed the intermediate type used in string conversion from numbers. 0
is present at the end of string, because deserializer uses Decimal
as the number type and when you try to run
210001.0M.ToString()
you get
210001.0
To revert to old handling which is Double
for numbers, you have to specify it explicitly when deserializing, eg.
JsonConvert.DeserializeObject<KeyValue>(json, new JsonSerializerSettings { FloatParseHandling = FloatParseHandling.Double });
This causes the deserializer to execute it as
210001D.ToString()
which returns
210001
Be aware that you might lose some precision when changing from Decimal
to Double
.
EDIT:
I have traced the problem to slightly changed implementation of JsonTextReader.ReadStringValue
. In version 8.0.3, it used the number type as an intermediate one, so FloatParseHandling
was used during deserialization from float
to string
. In version 9.0.1, it no longer used the number type, it just took a substring of JSON and set it as the value.
To sum up, I'm not familiar enough with JSON.net to suggest any other option than JsonConverter
from my other answer. Maybe someone more familiar with it could offer something else.
Upvotes: 4
Reputation: 1130
Solution which will definitely work is applying custom JsonConverter
to the property, which will give you the control over how value is serialized/deserialized.
First step is creating a new JsonConverter
, which is quite simple in implementation. I just invoke ToString()
on the value, which can be either a decimal
or double
.
public sealed class FloatStringConverter : JsonConverter
{
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotSupportedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return reader.Value.ToString();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(double) || objectType == typeof(decimal);
}
}
I made it so it can work with both values of FloatParseHandling
and it will format the values as I presented in my previous, incorrect answer. You can of course tweak it to your liking, if you want it to ignore the FloatParseHandling
but it will need type-checking to do it.
Just remember, that default value of FloatParseHandling
is FloatParseHandling.Double
, so it will work correctly without changing the serializer settings.
What you need to do next is mark the property with appropriate attribute so serializer will know to use it.
[JsonConverter(typeof(FloatStringConverter))]
public string Value { get; set; }
After this, Value
will contain the correct representation of number from JSON.
Upvotes: 4