在 Newtonsoft.Json 中处理十进制值 [英] Handling decimal values in Newtonsoft.Json

查看:39
本文介绍了在 Newtonsoft.Json 中处理十进制值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

已经快 5 年了,我不认为这是要走的路.客户应以正确的数字格式发布数据.使用 React 或 Angular 等当前框架,或者使用适当的架构和错误处理和验证,我认为这几乎不是问题.

It's been almost 5 years and I don't think this is the way to go. The client should post the data in the correct numerical format. With current frameworks like React or Angular, or with a proper architecture and error handling & validation, i think this is almost a non-problem.

但是,如果有人想展示他们的 Json.NET 肌肉,请随时查看答案.

But if anyone wishes to flex their Json.NET muscles, feel free to check the answers.

我有一个 MVC 应用程序,我在其中处理了一些 JSON.这很简单.我的 ModelBinder 中有这段简单的代码:

I have a MVC application and I handle some JSON in it. That's simple. I have this simple piece of code in my ModelBinder:

return JsonConvert.DeserializeObject(jsonString, bindingContext.ModelType, new JsonSerializerSettings
{
    NullValueHandling = NullValueHandling.Ignore,
    MissingMemberHandling = MissingMemberHandling.Ignore,
    Formatting = Formatting.None,
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    FloatParseHandling = FloatParseHandling.Decimal
});

而且它完美无瑕.

嗯,有点.

假设我有这个课程:

public class MyClass
{
    public decimal MyProp { get; set; }
}

如果我尝试反序列化这个 json:

If I try to deserialize this json:

"{"MyProp": 9888.77}"

当然可以,因为 9888.77 是 Javascript 浮点值.我想.

Of course it works, since 9888.77 is a Javascript float value. I think.

但是我的页面中有一个蒙面的钱输入,使 JSON 看起来像这样(对不起我的英语):

But I have a masked input for money in my page that makes the JSON look like this (sorry about my english):

"{"MyProp": "9.888,77" }"

AAAND,它失败了.它说它无法将字符串转换为十进制.

AAAND, it fails. It says that it Could not convert string to decimal.

好的,这很公平.它不是 JS 浮点数,但 Convert.ToDecimal("9.888,77") 按我想要的方式工作.

Ok, that's fair. It is not a JS float, but Convert.ToDecimal("9.888,77") works the way I want.

我在互联网上阅读了一些关于自定义反序列化器的教程,但对我来说,为我的应用程序中的每个类定义一个自定义反序列化器是不可行的.

I've read some tutorials on the internet about custom deserializers, but its inviable for me to define a custom deserializer for every single class I have in my application.

我想要的是简单地重新定义 JSON.Net 将字符串转换为十进制属性的方式,在我想要反序列化的任何类中.我想在转换小数的过程中注入Convert.ToDecimal函数,此时当前转换器不起作用.

What I want is to simple redefine the way JSON.Net converts a string to a decimal property, in any class i'll ever want to deserialize to. I want to inject the Convert.ToDecimal function in the process of converting decimals, when the current converter doesn't work.

有什么办法可以做到吗?

Is there a way I could do it?

我认为有办法做到这一点,所以我稍微更改了我的代码.

I thought there was a way to do it, so I changed my code a little bit.

JsonSerializer serializer = new JsonSerializer
{
    NullValueHandling = NullValueHandling.Ignore,
    MissingMemberHandling = MissingMemberHandling.Ignore,
    Formatting = Formatting.None,
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    FloatParseHandling = FloatParseHandling.Decimal,
};



return serializer.Deserialize(new DecimalReader(jsonStr), bindingContext.ModelType);

并创建了这个类:

public class DecimalReader : JsonTextReader
{
    public DecimalReader(string s)
        : base(new StringReader(s))
    {
    }

    public override decimal? ReadAsDecimal()
    {
        try
        {
            return base.ReadAsDecimal();
        }
        catch (Exception)
        {
            if (this.TokenType == JsonToken.String)
            {
                decimal value = 0;

                bool convertible = Decimal.TryParse(this.Value.ToString(), out value);

                if (convertible)
                {
                    return new Nullable<decimal>(value);
                }
                else { throw; }
            }
            else
            {
                throw;
            }
        }
    }
}

但它非常丑陋:它只在崩溃时执行我想要的,并且依赖于 base.ReadAsDecimal() 崩溃.简直不能更丑了.

But it is very ugly: it executes what I want only when it crashes, and depends on base.ReadAsDecimal() crashing. It couldn't be more ugly.

而且不起作用:将值1.231,23"转换为类型System.Nullable1[System.Decimal]"时出错.路径MyProp",X 行,Y 位置.

值本身正在被转换,但可能出于某种原因它仍然尝试将字符串1.231,23"转换为小数.

The value itself is being converted, but perhaps for some reason it still tries to put the string "1.231,23" into a decimal.

那么,有没有办法正确地做到这一点?

So, is there a way to do it properly?

推荐答案

您可以使用像这样的自定义 JsonConverter 类处理这两种格式(JSON 数字表示和屏蔽字符串格式).p>

You can handle both formats (the JSON number representation and the masked string format) using a custom JsonConverter class like this.

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Float || token.Type == JTokenType.Integer)
        {
            return token.ToObject<decimal>();
        }
        if (token.Type == JTokenType.String)
        {
            // customize this to suit your needs
            return Decimal.Parse(token.ToString(), 
                   System.Globalization.CultureInfo.GetCultureInfo("es-ES"));
        }
        if (token.Type == JTokenType.Null && objectType == typeof(decimal?))
        {
            return null;
        }
        throw new JsonSerializationException("Unexpected token type: " + 
                                              token.Type.ToString());
    }

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

要将其插入您的活页夹,只需将转换器的一个实例添加到 JsonSerializerSettings 对象的 Converters 列表中:

To plug this into your binder, just add an instance of the converter to the Converters list in the JsonSerializerSettings object:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    NullValueHandling = NullValueHandling.Ignore,
    MissingMemberHandling = MissingMemberHandling.Ignore,
    Formatting = Formatting.None,
    DateFormatHandling = DateFormatHandling.IsoDateFormat,
    Converters = new List<JsonConverter> { new DecimalConverter() }
};

这篇关于在 Newtonsoft.Json 中处理十进制值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆