C# 如何在Json.net中强制设置最小小数位数?

C# 如何在Json.net中强制设置最小小数位数?,c#,json,json.net,C#,Json,Json.net,当我使用json.net将小数写入json时,我会遇到一个恼人的不一致性。有时是1 dp,有时是2 dp 显然,我知道有一些解决方案可以将小数输出到具有一定数量小数的字符串中,例如,但如果不编写自定义序列化程序,就无法使用json.net进行控制 我也知道Math.Round为了强制执行最大小数位数,这个问题涉及强制执行最小小数位数 前两个测试显示了正在发生的事情,它保留了声明或计算中的原始小数位数 我发现我可以加一小部分,然后减去一小部分,接下来的两个测试表明这是有效的,但是有没有更干净的方法

当我使用json.net将小数写入json时,我会遇到一个恼人的不一致性。有时是1 dp,有时是2 dp

显然,我知道有一些解决方案可以将小数输出到具有一定数量小数的字符串中,例如,但如果不编写自定义序列化程序,就无法使用json.net进行控制

我也知道
Math.Round
为了强制执行最大小数位数,这个问题涉及强制执行最小小数位数

前两个测试显示了正在发生的事情,它保留了声明或计算中的原始小数位数

我发现我可以加一小部分,然后减去一小部分,接下来的两个测试表明这是有效的,但是有没有更干净的方法呢

[TestFixture]
public sealed class DecimalPlaces
{
    public class JsonType
    {
        public decimal Value { get; set; }
    }

    [Test]
    public void TwoDp()
    {
        var obj = new JsonType { Value = 1.00m };
        Assert.AreEqual("{\"Value\":1.00}", JsonConvert.SerializeObject(obj));
    }

    [Test]
    public void OneDp()
    {
        var json = new JsonType { Value = 1.0m };
        Assert.AreEqual("{\"Value\":1.0}", JsonConvert.SerializeObject(obj));
    }

    private decimal ForceMinimumDp(decimal p, int minDecimalPlaces)
    {
        decimal smallFrac = 1m/((decimal)Math.Pow(10, minDecimalPlaces));
        return p + smallFrac - smallFrac;
    }

    [Test]
    public void ForceMinimumTwoDp()
    {
        var obj = new JsonType { Value = ForceMinimumDp(1.0m, 2) };
        Assert.AreEqual("{\"Value\":1.00}", JsonConvert.SerializeObject(obj));
    }

    [Test]
    public void ForceMinimumThreeDp()
    {
        var obj = new JsonType { Value = ForceMinimumDp(1.0m, 3) };
        Assert.AreEqual("{\"Value\":1.000}", JsonConvert.SerializeObject(obj));
    }
}

您可以使用自定义JSON转换器执行此操作:

class DecimalJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof (decimal);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteRawValue(((decimal) value).ToString("F2", CultureInfo.InvariantCulture));
    }
}
这是一个非常基本的转换器。您可能需要对其进行扩展以支持其他浮点类型,甚至可能还支持整数类型

现在实例化serialiser并将其传递给自定义转换器,如下所示:

var serializer = new JsonSerializer();
serializer.Converters.Add(new DecimalJsonConverter());

我知道这是一个老问题,但我遇到了一个类似的问题,我正在自动验证JSON API响应,我需要强制执行至少3位小数。我想我会把我的通用解决方案留在这里,因为如果有类似的问题,它可能会帮助一些人并节省他们的时间

如果小数点后只有1或2位数字,则实际的JSON返回0并填充到小数点后3位

我预期的验证数据也需要归零 e、 g

我通过以下方式建立了我的预期,这为我解决了问题:

 public static decimal GetRate(
            string fromCurrency,
            string toCurrency,
            decimal rawRate,
            decimal margin)
        {
            if (fromCurrency == toCurrency) return 1m;

        var _rate = Math.Round(rawRate * (1 + (margin / 100)), 7);
        var _numberOfPlacesAfterDecimalPlace = _rate.ToString().Split('.')[1].Length;

        // NOTE: Software API response stores precision value to 3 decimal places, need to cater 
        // for that here for currency pairs where the resulting rate has less than 3 decimal places.
        // This will zero pad the result after the decimal place to 3 places
        if (_numberOfPlacesAfterDecimalPlace >= 3) { return _rate; }
        return Decimal.Parse(string.Format("{0:F3}", _rate));
    }

我的解决方案避免了我为这个特定问题编写自定义JSON转换器的需要。

1
1.0
1.00
通常在JSON中被视为等效的。这只是为了使显示更美观,还是JSON解析器真的将它们视为不同的?此外,加减
0.01
至少会强制执行两个小数,但不会用更多的小数对数字进行取整。这就是你想要的吗?您的问题向我建议您确实需要某种形式的舍入,要求强制执行一定数量的小数。@hvd不,只是我在手动比较json输出,因为小数点的准确性正在影响我,这很烦人。所以,是的,这是为了美观的原因。@hvd“但它不会用更多的小数取整数字”不,我知道怎么做。考虑这些数字已经被舍入到不再比我想要的小数位数。@ HVD我已经改变了标题,“我怎么能迫使小数位数最少……”HVD嗯,我期待其他的变化,所以深度相等将是错误的。当我进行文件比较时,我不想被所有这些小数位的更改击中,只想检查预期的更改是否正常。
 public static decimal GetRate(
            string fromCurrency,
            string toCurrency,
            decimal rawRate,
            decimal margin)
        {
            if (fromCurrency == toCurrency) return 1m;

        var _rate = Math.Round(rawRate * (1 + (margin / 100)), 7);
        var _numberOfPlacesAfterDecimalPlace = _rate.ToString().Split('.')[1].Length;

        // NOTE: Software API response stores precision value to 3 decimal places, need to cater 
        // for that here for currency pairs where the resulting rate has less than 3 decimal places.
        // This will zero pad the result after the decimal place to 3 places
        if (_numberOfPlacesAfterDecimalPlace >= 3) { return _rate; }
        return Decimal.Parse(string.Format("{0:F3}", _rate));
    }