c# - 在 Newtonsoft.Json 中处理十进制值

标签 c# javascript json json.net deserialization

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

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


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

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

而且它完美地工作。

嗯,有点。

假设我有这个类(class):

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

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

"{\"MyProp\": 9888.77}"

当然可以,因为 9888.77 是一个 Javascript 浮点值。我想。

但是我的页面中有一个用于金钱的屏蔽输入,这使得 JSON 看起来像这样(抱歉我的英语不好):

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

AAAND,它失败了。它说 Could not convert string to decimal

好吧,这很公平。它不是 JS float ,但 Convert.ToDecimal("9.888,77") 以我想要的方式工作。

我在 Internet 上阅读了一些关于自定义反序列化器的教程,但是我无法为我的应用程序中的每个类定义一个自定义反序列化器。

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

有什么办法可以做到吗?

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

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()崩溃。简直不能更丑了。

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

值本身正在被转换,但也许出于某种原因它仍然试图将字符串“1.231,23”转换为小数。

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

最佳答案

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

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();
    }
}

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

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

关于c# - 在 Newtonsoft.Json 中处理十进制值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24051206/

相关文章:

php - 在android中将php数组分成单独的Json响应

javascript - 如何找到表示两个数组有多少不同的百分比值?

javascript - jQuery -> getJSON -> 外部服务器上的数据加载失败 -> 仅 IE 问题 <- 所有其他浏览器工作

javascript - angularjs - 使用 JS 对象设置选择元素选项值

c# - User3 2's ShowWindow doesn' t 按预期行事

javascript - 奇怪的异步 node.js

javascript - 我想在 javascript 中生成这样的数组 [ 1, 2, ..., 9, 10 ]

c# - 如何在 C# 中比较字符串和枚举

c# - 当删除对类实例的所有其他引用时,Action<T> 是否会阻止封闭的类实例被 GC ?

c# - 禁用 Repeater 中的按钮