oleg chernyakov
oleg chernyakov

Reputation: 63

converting problems between doubles and strings in Newtonsoft.Json.net

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

Answers (2)

kiziu
kiziu

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

See demo at dotnetfiddle.net.

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

kiziu
kiziu

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

Related Questions