Json.NET 用最少的小数位序列化浮点/双精度,即没有多余的“.0”?

Posted

技术标签:

【中文标题】Json.NET 用最少的小数位序列化浮点/双精度,即没有多余的“.0”?【英文标题】:Json.NET serializing float/double with minimal decimal places, i.e. no redundant ".0"? 【发布时间】:2014-02-04 20:44:11 【问题描述】:

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

例如,运行这段代码时:

JsonConvert.SerializeObject(1.0);

我希望(并且想要)这个结果:

"1"

但是我得到了:

"1.0"

我查看了源代码,发现它是特意在提交0319263 中添加的(“...-Fixed 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";
    

因此,我想知道:

    发生这种变化的原因可能是什么? JSON specification 似乎不需要它。

    有没有简单的方法绕过它?

【问题讨论】:

Newtonsoft 已确认这是设计使然,请参阅 Decimal with no decimal point serialized as a Decimal with a decimal point #590。 【参考方案1】:

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

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;
    

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

    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;
    

无论哪种情况,要使用上面的代码,只需像这样调用序列化程序:

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

【讨论】:

这个方法对我来说失败了:CanConvert 一直返回 false 意味着自定义转换器无法正常工作——直到我密切关注这些类型并意识到我正在序列化可空类型。一旦我添加了对decimal?float?double? 的检查,它就开始工作了。谢谢! 虽然这个答案在大多数情况下都很好,但我想警告您有关 Convert.ToInt64 .. 如果您尝试 Convert.ToInt64(double.MaxValue) 并崩溃,它将溢出。 dotnetfiddle.net/8XbCir 如果你有浮点数,你会在IsWholeValue():Unable to cast object of type 'System.Single' to type 'System.Double'有异常。 .NET 6 你需要有“同样”的 IF 语句,但是对于浮点数 (float floatValue = (float)value)【参考方案2】:

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

规范没有要求,但也没有禁止。

我的猜测是,它允许对 Json.NET 进行更好的类型检查(如果他们在某处有它),或者它是一个“以防万一”的东西,允许区分整数和浮点类型。

2。有没有简单的方法绕过它?

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

【讨论】:

你从哪里得到这个流程图?它是规范的一部分吗?这似乎表明“000”不是数字。 流程图是此处所示流程图的早期版本:json.org/json-en.html。事实上,JSON 标准不允许带有前导零的数字。

以上是关于Json.NET 用最少的小数位序列化浮点/双精度,即没有多余的“.0”?的主要内容,如果未能解决你的问题,请参考以下文章

什么叫单精度浮点型?什么叫双精度浮点型?

精度浮点型数据精确到了几位小数呢?

MySQL 浮点数类型和定点数类型

变体浮点到双精度值转换,舍入到小数点后 1 位 [重复]

单精度小数点后面有几位?

c语言中啥是单精度型和双精度型???各举个例子。。。