如何使用 Json.NET 反序列化高精度十进制值?

Posted

技术标签:

【中文标题】如何使用 Json.NET 反序列化高精度十进制值?【英文标题】:How do I deserialize a high-precision decimal value with Json.NET? 【发布时间】:2016-12-16 08:06:11 【问题描述】:

我想将包含长十进制值的 JSON 反序列化为自定义类型以保持其精度(即自定义 BigDecimal 类)。我正在使用 Json.NET 9.0.1 和 .NET 4.6.1。我尝试过使用 JsonConverter,但似乎调用 ReadJson 时可用的值已被 Json.NET 识别并读取为 .NET 十进制类型,并且仅限于其精度。

理想情况下,我可以访问原始字符串,以便将其放入自定义类型中。我可以在目标对象上使用字符串属性,它会反序列化整个字符串,但是我必须进一步处理该对象(即,将其复制到另一个表示中),这在大型架构中尤其混乱。

对更好的方法有什么想法吗?


这是目标类:

public class DecimalTest

    public string stringValue  get; set; 
    public decimal decimalValue  get; set; 
    public BigDecimal bigDecimalValue  get; set; 

这是一个 JSON 测试:

    [TestMethod]
    public void ReadBigDecimal_Test()
    
        var json = @"
            ""stringValue"" : 0.0050000012852251529693603515625,
            ""decimalValue"" : 0.0050000012852251529693603515625,
            ""bigDecimalValue"" : 0.0050000012852251529693603515625
        ";

        var settings = new JsonSerializerSettings();
        settings.FloatParseHandling = FloatParseHandling.Decimal;
        settings.Converters.Add(new JsonBigDecimalConverter());

        var result = JsonConvert.DeserializeObject<DecimalTest>(json, settings);

        Assert.IsNotNull(result);
        Assert.AreEqual("0.0050000012852251529693603515625", result.stringValue);
        Assert.AreEqual(0.0050000012852251529693603516m, result.decimalValue);

        // *** This case fails ***
        Assert.AreEqual("0.0050000012852251529693603515625", result.bigDecimalValue.ToString());
    

这是自定义转换器:

public class JsonBigDecimalConverter : JsonConverter

    public override bool CanConvert(Type objectType)
    
        return (objectType == typeof(BigDecimal));
    

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    
        // *** reader.Value here already appears to be a .NET decimal.
        // *** If I had access to the raw string I could get this to work.
        return BigDecimal.Parse(reader.Value.ToString());
    

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    
        throw new NotImplementedException();
    

【问题讨论】:

【参考方案1】:

如果ReadJson 的以下实现按您的预期工作,您可以试试吗:

public override object ReadJson(
    JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)

    var token = JToken.Load(reader);
    return BigDecimal.Parse(token.ToString());

更新

很遗憾,上述方法不起作用。似乎没有办法从 JSON 数据中读取原始字符串。

还要注意,在我的测试中,stringValue 的断言首先失败。请参阅此工作示例:https://dotnetfiddle.net/s0pqg3

我认为这是因为 Json.NET 在内部会立即根据指定的 FloatParseHandling 解析它遇到的任何 number 令牌。原始数据永远不会被保留。

我认为唯一的解决方案是将大十进制字符串用引号括起来,如下所示:

"bigDecimalValue" : "0.0050000012852251529693603515625"

为了保持所需的精度,这是一个工作示例:https://dotnetfiddle.net/U1UG3z

【讨论】:

感谢您的关注。出于某种原因, stringValue 断言已经对我有用,而无需用引号对其进行限定。我还尝试了一个 .NET 4.5/console 版本来匹配您失败的 dotnetfiddle 示例;不知道有什么区别。

以上是关于如何使用 Json.NET 反序列化高精度十进制值?的主要内容,如果未能解决你的问题,请参考以下文章

为啥当我使用 JSON.NET 反序列化时会忽略我的默认值?

使用 Json.Net 进行 C# 枚举反序列化:将值转换为类型时出错

反序列化缺少第一列的数据表

使用JSON.Net将字符串属性值反序列化为类实例

使用 JSON.NET 反序列化 DateTime 时如何保留时区? [复制]

如何使用 Json.Net 序列化/反序列化带有自定义键的字典?