Json.NET使用最小的小数位序列化float/double,即没有多余的".0"?

Ari*_*ieh 19 .net floating-point json json.net

序列化浮点数和双精度数时,如果数字不包含任何小数部分,Json.NET总是在末尾添加".0".我想知道是否有一种简单的方法可以绕过这一点,从而产生更紧凑的表现形式?序列化包含许多数字的对象时,会出现额外的句点和零.

例如,运行此代码时:

JsonConvert.SerializeObject(1.0);
Run Code Online (Sandbox Code Playgroud)

我希望(并希望)这个结果:

"1"
Run Code Online (Sandbox Code Playgroud)

但相反,我得到:

"1.0"
Run Code Online (Sandbox Code Playgroud)

我查看了源代码并注意到它是故意添加在提交0319263("... - 修复JsonConvert总是写一个带小数位的浮点数......"),它运行的代码基本上看起来像:

    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";
    }
Run Code Online (Sandbox Code Playgroud)

因此,我想知道:

  1. 可能是这种变化的原因是什么?的JSON规范似乎并不需要它.

  2. 是否有一种简单的方法可以绕过它?

小智 23

作为问题2的替代答案(假设您不想经历编译自己的Json.NET源自定义版本的麻烦),您可以创建自己的自定义JsonConverter类来处理decimal,float和double值.这是我正在使用的版本:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这将保留十进制类型值的精度.如果您希望忽略十进制值的精度,可以使IsWholeValue()函数的小数部分与float/double部分相同:

    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;
    }
Run Code Online (Sandbox Code Playgroud)

在任何一种情况下,要使用上面的代码,只需像这样调用序列化程序:

string json = JsonConvert.SerializeObject(value, new DecimalJsonConverter())
Run Code Online (Sandbox Code Playgroud)

  • 这个方法对我来说失败了:`CanConvert` 一直返回 false 意味着自定义转换器不工作——直到我密切关注类型并意识到我正在序列化可为空类型。一旦我添加了对“decimal?”、“float?”和“double?”的检查,它就开始工作了。谢谢! (3认同)
  • 虽然这个答案在大多数情况下都非常好,但我想警告您有关 Convert.ToInt64 的问题。如果您尝试 Convert.ToInt64(double.MaxValue) 并崩溃,它将溢出。https://dotnetfiddle.net/8XbCir (2认同)

小智 5

1.这种变化可能是什么原因?

规格不要求它,但它也不允许它.

我的猜测是它允许对Json.NET进行更好的类型检查(如果它们在某处),或者它是一个"只是在案例"的东西,允许区分整数和浮点类型.

来自Json.org

2.有没有一种简单的方法可以绕过它?

不是那么容易,但如果你真的想要它,你可以在改成EnsureDecimalPlace()简单后重新编译你自己的Json.NET版本return text;