如何在Json.net中强制最小小数位?

时间:2016-01-02 18:00:20

标签: c# json json.net

当我使用json.net向json写小数时,我会遇到恼人的不一致。有时它达到1 dp,有时则为2。

显然我已经意识到将小数输出到具有一定数量小数的字符串的解决方案,例如this,但是如果不编写自定义序列化程序,则不能使用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));
    }
}

2 个答案:

答案 0 :(得分:14)

您可以使用自定义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));
    }
}

这是一个非常基本的转换器。您可能需要扩展它以支持其他浮点类型,或者甚至是整数类型。

现在实例化您的序列化程序并将其传递给您的自定义转换器,如下所示:

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

答案 1 :(得分:1)

我知道这是一个古老的问题,但是我有一个类似的问题,在该问题中,我要自动验证JSON API响应,因此我需要强制至少保留3个小数位。我以为我会在这里留下一些通用的解决方案,因为这可能会帮助某个人,并在他们遇到类似问题时为他们节省时间。

如果小数点后仅1或2位,则实际JSON返回零,填充到小数点后3位。

然后,我期望的验证数据也需要零填充 例如

10.3   pad to 10.300
10.34  pad to 10.340
10.345 leave as is as will match the actual

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

 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转换器。