Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
414 views
in Technique[技术] by (71.8m points)

.net - Json.NET serializing float/double with minimal decimal places, i.e. no redundant ".0"?

When serializing floats and doubles, Json.NET always adds ".0" at the end if the number doesn't contain any fractional part. I was wondering if there was an easy way to bypass this, to result in a more compact representation? The extra periods and zeroes add up when serializing an object containing many numbers.

For example, when running this code:

JsonConvert.SerializeObject(1.0);

I would expect (and want) this result:

"1"

But instead I get:

"1.0"

I looked at the source code and noticed that it was purposely added in commit 0319263 ("...-Fixed JsonConvert to always write a floating point number with a decimal place...") where it runs code that looks basically like:

    private static string EnsureDecimalPlace(double value, string text)
    {
        if (double.IsNaN(value) || double.IsInfinity(value) ||
            text.IndexOf('.') != -1 || text.IndexOf('E') != -1 ||
            text.IndexOf('e') != -1)
        {
            return text;
        }

        return text + ".0";
    }

Consequently, I am wondering:

  1. What may have been the reason for that change? The JSON specification does not seem to require it.

  2. Is there an easy way to bypass it?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As an alternative answer to question 2 (assuming you don't want to go through the hassle of compiling your own custom version of the Json.NET source) you can create your own custom JsonConverter class to handle decimal, float, and double values. Here's the version I'm using:

class DecimalJsonConverter : JsonConverter
{
    public DecimalJsonConverter()
    {
    }

    public override bool CanRead
    {
        get
        {
            return false;
        }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(decimal) || objectType == typeof(float) || objectType == typeof(double));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (DecimalJsonConverter.IsWholeValue(value))
        {
            writer.WriteRawValue(JsonConvert.ToString(Convert.ToInt64(value)));
        }
        else
        {
            writer.WriteRawValue(JsonConvert.ToString(value));
        }
    }

    private static bool IsWholeValue(object value)
    {
        if (value is decimal)
        {
            decimal decimalValue = (decimal)value;
            int precision = (Decimal.GetBits(decimalValue)[3] >> 16) & 0x000000FF;
            return precision == 0;
        }
        else if (value is float || value is double)
        {
            double doubleValue = (double)value;
            return doubleValue == Math.Truncate(doubleValue);
        }

        return false;
    }
}

This will preserve the precision on values of type decimal. If you would prefer to ignore the precision of decimal values, you can make the decimal portion of the IsWholeValue() function work the same as the float/double portion:

    private static bool IsWholeValue(object value)
    {
        if (value is decimal)
        {
            decimal decimalValue = (decimal)value;
            return decimalValue == Math.Truncate(decimalValue);
        }
        else if (value is float || value is double)
        {
            double doubleValue = (double)value;
            return doubleValue == Math.Truncate(doubleValue);
        }

        return false;
    }

In either case, to use the above code, just call the serializer like this:

string json = JsonConvert.SerializeObject(value, new DecimalJsonConverter())

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...